﻿<template>
  <view class="app-container">
    <!-- 返回上一页 -->
    <u-navbar :title="pageTitle" autoBack leftIcon="arrow-left" fixed safeAreaInsetTop />
    <AdaptiveNavBar :title="pageTitle" text-color="#333333" backgroundColor="#ffffff" :showHome="true" />
    <!-- 页面内容 -->
    <scroll-view scroll-y class="content" :style="{ marginTop: $navbarUtil.getNavbarTotalHeightRpx() }">
      <!-- 经营协议 -->
      <view class="card">
        <view class="card-header">经营协议</view>
        <view class="card-body" style="margin-right: 0;background: #f8f8f8;padding: 10rpx 0rpx;">
          <view class="">
            为确保合法运营该产品/服务，您须事先阅读并同意
            <block v-for="(agreement, index) in getAgreements" :key="index">
              <text @click="showAgreement(agreement)" class="agreement-link">《{{ agreement }}》</text>
              <text v-if="index < getAgreements.length - 1" class="agreement-text">、</text>
            </block>
            并在上架销售、接受调用/合作/预订前勾选确认其法律效力。
          </view>
        </view>
      </view>
      <!-- 首图 / 轮播图上传 -->
      <view class="card">
        <view class="card-header">
          <span>首图 / 轮播图</span>
          <span style="margin-left: auto; color: #999; font-size: 24rpx; text-align: right;">{{ form.images.length }}/{{
            isApplyCall ? initialImageCount + 4 : maxImages }}</span>
        </view>

        <view class="card-body">
          <view class="image-upload">
            <view v-for="(img, index) in form.images" :key="index" class="image-item">
              <image :src="img.url" mode="aspectFill" />
              <view v-if="!isBusinessType3 && canDeleteImage(img)" class="delete" @click="confirmRemove(index)">
                ×
              </view>
              <!-- 图片上传者信息 -->
              <!-- <view v-if="img.uploadedBy" class="image-info">
                <text class="uploader-text">{{ getImageUploaderText(img.uploadedBy) }}</text>
              </view> -->
            </view>
            <view v-if="canUploadMore && !isBusinessType3" class="upload-btn" @click="chooseImage">
            </view>
          </view>

        </view>
      </view>

      <!-- 研学旅行类别标签 -->
      <view class="card">
        <view class="card-header">研学主题类别标签</view>
        <view class="card-body" style="display: flex;align-items: center;justify-content: flex-end;">
          <!-- 一级和二级分类选择在同一行 -->
          <view class="category-container">
            <!-- 主标签选择 -->
            <view class="category-item">
              <picker mode="selector" :range="mainCategoryPickerOptions" :value="mainCategoryPickerValue"
                @change="onMainCategoryChange" :disabled="disableCategorySelection">
                <view :class="['category-picker', { 'category-picker--disabled': disableCategorySelection, 'category-picker--selected': selectedMainCategoryDisplay && !disableCategorySelection }]"><text
                    class="category-picker__text" :class="{ 'is-placeholder': !selectedMainCategoryDisplay }">{{
                    selectedMainCategoryDisplay || "请选择主标签" }}</text>
                  <image v-if="!disableCategorySelection" class="category-picker__arrow"
                    src="../../static/systemIcon/arrow-bottom.png" mode="aspectFit" />
                </view>
              </picker>
            </view>

            <!-- 子标签选择 - 始终显示 -->
            <template v-if="!isCustomCategoryMode">
              <view class="category-item">
                <picker mode="selector"
                  :range="currentSubCategories.length > 0 ? currentSubCategories.map(sub => sub.text) : ['请选择子标签']"
                  @change="onSubCategoryChange" :disabled="!hasStandardMainCategory || disableCategorySelection">
                  <view
                    :class="['category-picker', { 'category-picker--disabled': !hasStandardMainCategory || disableCategorySelection, 'category-picker--selected': selectedSubCategoryDisplay && hasStandardMainCategory && !disableCategorySelection }]">
                    <text class="category-picker__text" :class="{ 'is-placeholder': !selectedSubCategoryDisplay }">{{
                      selectedSubCategoryDisplay || "请选择子标签" }}</text>
                    <image v-if="!disableCategorySelection && hasStandardMainCategory" class="category-picker__arrow"
                      src="../../static/systemIcon/arrow-bottom.png" mode="aspectFit" />
                  </view>
                </picker>
              </view>
            </template>
            <template v-else>
              <view class="custom-category-inputs">
                <input class="custom-category-input" v-model="form.customMainCategory"
                  :disabled="disableCategorySelection" placeholder="请输入主标签" />
                <input class="custom-category-input" v-model="form.customSubCategory"
                  :disabled="disableCategorySelection" placeholder="请输入子标签" />
              </view>
            </template>
          </view>
        </view>
      </view>

      <!-- 产品标题 -->
      <view class="card" style="position: relative;">

        <view class="card-header" style="display: flex;align-items: center;justify-content: space-between;">
          <view>产品/服务标题</view>
          <view class="input-count">{{ form.name.length }}/100</view>
        </view>

        <view class="card-body" style="padding-right: 20rpx;flex:1">
          <textarea class="input textarea-input" maxlength="100" placeholder="请填写产品/服务标题" v-model="form.name"
            :auto-height="true" :min-height="80" :max-height="300" :disabled="disableTitleInput" @input="onTitleInput" />
        </view>
      </view>

      <!-- 产品特色（可展开卡片） -->
      <view class="card">
        <view class="card-header expandable" @click="toggleSection('feature')">
          <view class="expense-header-content">
            <text class="expense-title-text">产品特色</text>
            <view class="expense-status-container">
              <view :class="moduleStatus.feature() ? 'expense-status-label-completed' : 'expense-status-label'">
                {{ moduleStatus.feature() ? '已填写' : '待完善' }}
              </view>
            </view>
          </view>
        </view>
        <view class="card-body" v-if="expanded.feature">
          <!-- 内容预览区域 - 添加青色虚线框表示可编辑态 -->
          <view class="editable-preview-box" :class="{ 'disabled': shouldDisableFeature }" @click="handleFeatureClick">
            <!-- 使用rich-text组件渲染HTML内容 -->
            <template
              v-if="productStore.subPageData?.productFeaturesHtml && productStore.subPageData.productFeaturesHtml.trim()">
              <view class="preview-content">
                <scroll-view scroll-y="true" class="scrollable-content" style="padding: 0rpx 10rpx;"
                  :show-scrollbar="true" enhanced :bounce="true">
                  <rich-text :nodes="normalizedProductFeaturesHtml" class="feature-rich-text"></rich-text>
                  <view class="bottom-spacer"></view>
                </scroll-view>
              </view>
            </template>
            <template
              v-else-if="productStore.subPageData?.productFeatures && productStore.subPageData.productFeatures.length > 0">
              <view class="preview-content">
                <scroll-view scroll-y="true" class="scrollable-content" style="padding: 0rpx 10rpx;"
                  :show-scrollbar="true" enhanced :bounce="true">
                  <view v-for="(feature, index) in sortedProductFeatures" :key="index" class="feature-item">
                    <view v-if="feature.type === 'text'" class="feature-text">{{ feature.alt }}</view>
                    <image v-else-if="feature.type === 'image'" :src="feature.imgUrl" mode="widthFix"
                      class="feature-image" />
                  </view>
                  <view class="bottom-spacer"></view>
                </scroll-view>
              </view>
            </template>
            <template v-else>
              <view class="no-content">
                <text class="preview-text">点击进入内页编辑</text>
              </view>
            </template>
          </view>
        </view>
      </view>

      <!-- 产品特色自定义（可展开卡片） -->
      <view class="card" v-if="shouldDisableFeature">
        <view class="card-header expandable" @click="toggleSection('featureCustom')">
          <view class="expense-header-content">
            <text class="expense-title-text">产品特色（组团附加）</text>
            <view class="expense-status-container">
              <view :class="moduleStatus.featureCustom() ? 'expense-status-label-completed' : 'expense-status-label'">
                {{ moduleStatus.featureCustom() ? '已填写' : '待完善' }}
              </view>
            </view>
          </view>
        </view>
        <view class="card-body" v-if="expanded.featureCustom">
          <!-- 内容预览区域 - 添加青色虚线框表示可编辑态 -->
          <view class="editable-preview-box" @click="handleFeatureCustomClick">
            <!-- 使用rich-text组件渲染HTML内容 -->
            <template
              v-if="productStore.subPageData?.productFeaturesCustomHtml && productStore.subPageData.productFeaturesCustomHtml.trim()">
              <view class="preview-content">
                <scroll-view scroll-y="true" class="scrollable-content" style="padding: 0rpx 10rpx;"
                  :show-scrollbar="true" enhanced :bounce="true">
                  <rich-text :nodes="normalizedProductFeaturesCustomHtml" class="feature-rich-text"></rich-text>
                  <view class="bottom-spacer"></view>
                </scroll-view>
              </view>
            </template>
            <template
              v-else-if="productStore.subPageData?.productFeaturesCustom && productStore.subPageData.productFeaturesCustom.length > 0">
              <view class="preview-content">
                <scroll-view scroll-y="true" class="scrollable-content" style="padding: 0rpx 10rpx;"
                  :show-scrollbar="true" enhanced :bounce="true">
                  <view v-for="(feature, index) in sortedProductFeaturesCustom" :key="index" class="feature-item">
                    <view v-if="feature.type === 'text'" class="feature-text">{{ feature.alt }}</view>
                    <image v-else-if="feature.type === 'image'" :src="feature.imgUrl" mode="widthFix"
                      class="feature-image" />
                  </view>
                  <view class="bottom-spacer"></view>
                </scroll-view>
              </view>
            </template>
            <template v-else>
              <view class="no-content">
                <text class="preview-text">点击进入内页编辑</text>
              </view>
            </template>
          </view>
        </view>
      </view>

      <!-- 具体行程（可展开卡片） -->
      <view class="card">
        <view class="card-header expandable" @click="toggleSection('itinerary')">
          <view class="expense-header-content">
            <text class="expense-title-text">具体行程</text>
            <view class="expense-status-container">
              <view :class="moduleStatus.itinerary() ? 'expense-status-label-completed' : 'expense-status-label'">
                {{ moduleStatus.itinerary() ? '已填写' : '待完善' }}
              </view>
            </view>
          </view>
        </view>
        <view class="card-body" v-if="expanded.itinerary">
          <!-- 行程预览区域 - 添加青色虚线框表示可编辑态，16:9固定比例 -->
          <view class="editable-preview-box aspect-ratio-16-9" :class="{ 'disabled': isBusinessType3 }"
            @click="handleItineraryClick">
            <!-- 使用rich-text组件渲染HTML内容 -->
            <template
              v-if="productStore.subPageData?.specificItinerary && productStore.subPageData.specificItinerary.length > 0">
              <view class="preview-content" style="padding: 0;">
                <scroll-view scroll-y="true" class="scrollable-content" style="padding: 0rpx;" :show-scrollbar="true"
                  enhanced :bounce="true">
                  <view v-for="(dayItem, dayIndex) in itinerarySummary" :key="dayIndex" class="day-card">
                    <view class="day-header">
                      <view class="day-tag-container">
                        <view class="day-badge">D{{ dayIndex + 1 }}</view>
                        <view class="day-item-title">{{ dayItem.title }}</view>
                      </view>
                    </view>

                    <view class="day-details" v-if="dayItem.items && dayItem.items.length > 0">
                      <view v-for="(item, itemIndex) in dayItem.items" :key="itemIndex" class="itinerary-detail-item">
                        <view class="item-time-container">
                          <text class="item-time">{{ item.startTime || '10:30' }}</text>
                          <text class="item-type">{{ item.title || '活动' }}</text>
                          <text class="item-separator">|</text>
                          <text class="item-name">{{ item.name || item.alt || '未填写标题' }}</text>
                        </view>

                        <view class="item-details">
                          <view class="item-detail-row">
                            <view class="detail-label">
                              <FontIcon name="icon-time" class="detail-label-icon"></FontIcon>
                            </view>
                            <text class="detail-value">{{ item.startTime }} - {{ item.endTime }}</text>
                          </view>
                          <view class="item-detail-row">
                            <view class="detail-label">
                              <FontIcon name="icon-location" class="detail-label-icon"></FontIcon>
                            </view>
                            <view class="detail-value address-display">
                              <text v-if="getAddressName(item.address)" class="address-name">{{
                                getAddressLocation(item.address) }}</text>
                              <text v-if="getAddressLocation(item.address)" class="address-location">{{
                                getAddressLocation(item.address) }}</text>
                            </view>
                          </view>
                          <view class="item-detail-row" v-if="item.description">
                            <view class="detail-label">
                              <FontIcon name="icon-intro" class="detail-label-icon"></FontIcon>
                            </view>
                            <text class="detail-value">{{ item.description }}</text>
                          </view>
                        </view>
                      </view>
                    </view>
                  </view>
                  <view class="bottom-spacer"></view>
                </scroll-view>
              </view>
            </template>
            <template v-else>
              <view class="no-content">
                <text class="preview-text">点击进入内页编辑</text>
              </view>
            </template>
          </view>
        </view>
      </view>

      <!-- 费用包含（可展开卡片） -->
      <view class="card">
        <!-- 服务详情分割线 -->
        <view class="service-detail-divider">
          <text class="service-detail-text">服务详情</text>
        </view>
        <view class="card-header expandable" @click="toggleSection('included')">
          <view class="expense-header-content">
            <text class="expense-title-text">费用包含</text>
            <view class="expense-status-container">
              <view :class="moduleStatus.included() ? 'expense-status-label-completed' : 'expense-status-label'">
                {{ moduleStatus.included() ? '已填写' : '待完善' }}
              </view>
              <!-- <text class="expense-arrow">></text> -->
            </view>
          </view>
        </view>
        <view class="card-body" v-if="expanded.included">
          <!-- 费用包含预览区域 - 添加青色虚线框表示可编辑态，16:9固定比例 -->
          <view class="editable-preview-box aspect-ratio-16-9" :class="{ 'disabled': isBusinessType3 }"
            @click="handleExpenseClick('included')">
            <!-- 有内容时的预览 -->
            <view v-if="productStore.subPageData?.expensesIncluded.length > 0" class="preview-content">
              <scroll-view scroll-y="true" class="scrollable-content" :show-scrollbar="true" enhanced :bounce="true">
                <view v-for="(item, index) in productStore.subPageData?.expensesIncluded" :key="index"
                  class="expense-item">
                  <view class="expense-title">{{ item.title }}</view>
                  <view class="expense-content">{{ item.alt }}</view>
                  <view class="expense-content">{{ item.supplement ? item.supplement : '' }}</view>
                </view>
                <view class="bottom-spacer"></view>
              </scroll-view>
            </view>
            <!-- 无内容时的提示 -->
            <view v-else class="no-content">
              <!-- <text class="preview-text">暂无费用包含的内容或摘要</text> -->
              <text class="preview-hint">点击进入内页编辑</text>
            </view>
          </view>
        </view>
      </view>

      <!-- 费用不含（可展开卡片） -->
      <view class="card">
        <view class="card-header expandable" @click="toggleSection('excluded')">
          <view class="expense-header-content">
            <text class="expense-title-text">费用不含</text>
            <view class="expense-status-container">
              <view :class="moduleStatus.excluded() ? 'expense-status-label-completed' : 'expense-status-label'">
                {{ moduleStatus.excluded() ? '已填写' : '待完善' }}
              </view>
              <!-- <text class="expense-arrow">></text> -->
            </view>
          </view>
        </view>
        <view class="card-body" v-if="expanded.excluded">
          <!-- 费用不含预览区域 - 添加青色虚线框表示可编辑态，16:9固定比例 -->
          <view class="editable-preview-box aspect-ratio-16-9" :class="{ 'disabled': isBusinessType3 }"
            @click="handleExpenseClick('excluded')">
            <!-- 有内容时的预览 -->
            <view v-if="getExpensesExcludedContent().length > 0" class="preview-content">
              <scroll-view scroll-y="true" class="scrollable-content" :show-scrollbar="true" enhanced :bounce="true">
                <view class="expense-content-text">{{ getExpensesExcludedContent() }}</view>
                <view class="bottom-spacer"></view>
              </scroll-view>
            </view>
            <!-- 无内容时的提示 -->
            <view v-else class="no-content">
              <!-- <text class="preview-text">暂无费用不含的内容或摘要</text> -->
              <text class="preview-hint">点击进入内页编辑</text>
            </view>
          </view>
        </view>
      </view>

      <!-- 另付费项目（可展开卡片） -->
      <view class="card">
        <view class="card-header expandable" @click="toggleSection('other')">
          <view class="expense-header-content">
            <text class="expense-title-text">另付费项目</text>
            <view class="expense-status-container">
              <view :class="moduleStatus.other() ? 'expense-status-label-completed' : 'expense-status-label'">
                {{ moduleStatus.other() ? '已填写' : '待完善' }}
              </view>
              <!-- <text class="expense-arrow">></text> -->
            </view>
          </view>
        </view>
        <view class="card-body" v-if="expanded.other">
          <!-- 另付费项目预览区域 - 添加青色虚线框表示可编辑态，16:9固定比例 -->
          <view class="editable-preview-box aspect-ratio-16-9" :class="{ 'disabled': isBusinessType3 }"
            @click="handleExpenseClick('other')">
            <!-- 有内容时的预览 -->
            <view v-if="getOtherExpensesContent().length > 0" class="preview-content">
              <scroll-view scroll-y="true" class="scrollable-content" :show-scrollbar="true" enhanced :bounce="true">
                <view class="expense-content-text">{{ getOtherExpensesContent() }}</view>
                <view class="bottom-spacer"></view>
              </scroll-view>
            </view>
            <!-- 无内容时的提示 -->
            <view v-else class="no-content">
              <!-- <text class="preview-text">暂无另付费项目的内容或摘要</text> -->
              <text class="preview-hint">点击进入内页编辑</text>
            </view>
          </view>
        </view>
      </view>
      <!-- 预定限制 -->
      <view class="card" style="border-radius: 10rpx; overflow: hidden;">
        <!-- 预订须知分割线 -->
        <view class="service-detail-divider">
          <text class="service-detail-text">预订须知</text>
        </view>
        <!-- 标题 -->
        <view class="card-header expandable" @click="toggleSection('bookingInfo')">
          <view class="expense-header-content">
            <text class="expense-title-text">预订限制</text>
            <view class="expense-status-container">
              <view
                :class="(form.minYearOld != 3 || form.maxYearOld != 18) ? 'expense-status-label-completed' : 'expense-status-label'">
                {{ (form.minYearOld != 3 || form.maxYearOld != 18) ? '已限制' : '未限制' }}
              </view>
            </view>
          </view>
        </view>

        <view class="card-body" style="padding: 0;" v-if="expanded.bookingInfo">
          <!-- 参营年龄限制 -->
          <view
            style="box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2);padding: 20rpx 30rpx 20rpx 20rpx; border-bottom: 1rpx solid #f0f0f0; background-color: #F8F8F8; margin-bottom: 20rpx;">
            <view style="display: flex; align-items: center; margin-bottom: 15rpx;">
              <view
                style="width: 40rpx; height: 40rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-right: 15rpx;">
                <FontIcon name="icon-svg-nianling" :size="24"></FontIcon>
              </view>
              <text class="bottom-text-color" style="font-size: 28rpx;font-weight: 700;">参营年龄限制 <text
                  style="margin-left: 100rpx;">（ {{ form.minYearOld }} - {{ form.maxYearOld }}岁 ）</text></text>
              <!-- <text class="bottom-text-color" style="margin-left: 200rpx; color: #666;">（ 6 -11岁 ）</text> -->
            </view>
            <view style="margin-left: 55rpx;">
              <up-slider :rangeValue="ageRange" :min="ageMinLimit" :max="ageMaxLimit" :step="1" isRange :show-value="true"
                active-color="#48D184" :disabled="isBusinessType3" @change="onAgeRangeChange"></up-slider>
            </view>
          </view>

          <!-- 家长陪同参营 -->
          <view
            style="padding: 20rpx 30rpx 20rpx 20rpx; background-color: #F8F8F8;box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2);">
            <view style="display: flex; align-items: center; justify-content: space-between;">
              <view style="display: flex; align-items: center;">
                <view
                  style="width: 40rpx; height: 40rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin-right: 15rpx;">
                  <FontIcon name="icon-svg-jiachangpeitong" :size="28"></FontIcon>
                </view>
                <text class="bottom-text-color" style="font-size: 28rpx;font-weight: 700;">家长陪同参营（独立营~不可陪同）</text>
              </view>
              <view v-if="!isBusinessType3" class="switch-disabled" @click="handleParentSwitchChange($event)"
                style="width: 80rpx; height: 40rpx; background-color: #E0E0E0; border-radius: 20rpx; position: relative; display: flex; align-items: center; padding: 0 4rpx;">
                <view
                  style="width: 32rpx; height: 32rpx; background-color: #FFFFFF; border-radius: 50%; position: absolute; left: 4rpx;">
                </view>
              </view>

            </view>
          </view>
        </view>
      </view>

      <!-- 保障提示（不可编辑，固定内容） -->
      <view class="card">
        <view class="card-header expandable" @click="toggleSection('guaranteePrompt')">
          <view class="expense-header-content">
            <text class="expense-title-text">保障提示</text>
            <view class="expense-status-container">
              <view class="expense-status-label-completed">平台规则</view>
            </view>
          </view>
        </view>
        <view class="card-body guarantee-content" v-if="expanded.guaranteePrompt">
          <!-- 保障提示固定内容 -->
          <view class="text-content guarantee-fixed">
            <text>为切实履行好普通消费者权益保护等方面的义务，纤程旅行会基于安全和公平交易原则对包括但不限于扰乱系统、网络攻击、刷单、倒卖、转售订单及包括但不限于注册账号、用户信息、交易数量存在异常的订单进行风险交易判定，此部分不符合交易安全和公平原则的订单纤程旅行有权采取警示、暂停或者终止服务等措施且不承担因此原因取消订单的任何责任。</text>
          </view>
        </view>
      </view>

      <!-- 成团/退团/违约条款（可展开卡片） 只有二类用户可看  暂时解除限制 进行编辑调整-->
      <view class="card" v-if="shouldShowGroupRules" @click.stop="showGroupRulesModal">
        <view class="card-header expandable">
          <view style="flex: 1; display: flex; justify-content: space-between; align-items: center;">
            <view style="display: flex; align-items: center; flex: 1;">
              <text style="flex:1">成团/退团/违约条款<text style="font-size: 24rpx;color: #787878">（{{ groupRulesData.modeText
                  ||
                  '请选择模式' }}）</text></text>
              <view class="expense-status-container" style="margin-left: 20rpx;">
                <view :class="moduleStatus.groupRules() ? 'expense-status-label-completed' : 'expense-status-label'">
                  {{ moduleStatus.groupRules() ? '已填写' : '待完善' }}
                </view>
              </view>
            </view>
            <!-- <text class="arrow">{{ expanded.groupRules ? '▲' : '▼' }}</text> -->
          </view>
          <!-- <view class="edit-button" @click="showGroupRulesModal">
            <text>编辑</text>
          </view> -->
        </view>
        <view class="card-body" v-if="expanded.groupRules">
          <!-- 成团/退团/违约条款预览区域 -->
          <view class="editable-preview-box group-rules-preview" :class="{ 'disabled': isBusinessType3 }"
            @click="handleGroupRulesClick">
            <scroll-view scroll-y="true" class="scrollable-content" :show-scrollbar="true" enhanced :bounce="true">
              <view class="text-content">
                <!-- 动态显示条款内容 -->
                <view v-if="groupRulesData.mode" class="group-rules-content">
                  <view style="margin-bottom: 20rpx;">
                    <view style="font-weight: bold; margin-bottom: 10rpx;">成团人数</view>
                    <view style="margin-left: 20rpx; line-height: 40rpx;">
                      每期最低成团人数为<span style="color: #ff6600; font-weight: bold;">-{{
                        groupRulesData.leastNumberOfPeople}}-</span>人。
                    </view>
                    <view style="margin-left: 20rpx; line-height: 40rpx;">
                      每团最多满员人数为<span style="color: #ff6600; font-weight: bold;">-{{
                        groupRulesData.fullNumberOfPeople}}-</span>人。
                    </view>
                  </view>

                  <view style="margin-bottom: 20rpx;">
                    <view style="font-weight: bold; margin-bottom: 10rpx;">成团确认</view>
                    <view style="margin-left: 20rpx; line-height: 40rpx;">
                      组织方将在行程日期前<span style="color: #ff6600; font-weight: bold;">-{{
                        groupRulesData.applicationDeadline
                      }}-</span>天确认是否成团。
                    </view>
                    <view style="margin-left: 20rpx; line-height: 40rpx;">
                      一旦确认成团，则默认发团。
                    </view>
                  </view>

                  <view style="margin-bottom: 20rpx;">
                    <view style="font-weight: bold; margin-bottom: 10rpx;">未成团处理</view>
                    <view style="margin-left: 20rpx; line-height: 40rpx;">
                      未达到成团人数或通知不成团将默认全额退款、退团。钱数会在7个工作日内按原路退回。
                    </view>
                  </view>

                  <view style="margin-bottom: 20rpx;">
                    <view style="font-weight: bold; margin-bottom: 10rpx;">参营人员退团政策</view>
                    <view style="margin-left: 20rpx; line-height: 40rpx;">
                      在行程前解除合同的，违约金扣除标准为：
                    </view>
                    <view class="rule-table">
                      <view class="rule-table-header">
                        <view class="rule-table-cell">退团时间</view>
                        <view class="rule-table-cell">违约金</view>
                      </view>
                      <view v-for="(policy, index) in groupRulesData.policies" :key="index" class="rule-table-row">
                        <view class="rule-table-cell">行程前{{ policy.days }}</view>
                        <view class="rule-table-cell" style="color: #ff6600;">{{ policy.percentage }}%
                        </view>
                      </view>
                    </view>
                  </view>
                </view>

                <!-- 未选择模式时的提示 -->
                <view v-else class="empty-prompt">
                  <text>请点击编辑按钮选择条款模式</text>
                </view>
              </view>
              <view class="bottom-spacer"></view>
            </scroll-view>
          </view>
        </view>
      </view>
      <!-- 风险提示（可编辑） -->
      <view class="card">
        <view class="card-header expandable" @click="toggleSection('riskPrompt')">
          <view class="expense-header-content">
            <text class="expense-title-text">风险提示</text>
            <view class="expense-status-container">
              <view :class="moduleStatus.riskPrompt() ? 'expense-status-label-completed' : 'expense-status-label'">
                {{ moduleStatus.riskPrompt() ? '已填写' : '待完善' }}
              </view>
            </view>
          </view>
        </view>
        <view class="card-body" v-if="expanded.riskPrompt">
          <!-- 风险提示预览区域 - 添加青色虚线框表示可编辑态，16:9固定比例 -->
          <view class="editable-preview-box aspect-ratio-16-9" :class="{ 'disabled': isBusinessType3 }"
            @click="handleRiskPromptClick">
            <!-- 有内容时的预览 -->
            <view v-if="getRiskPromptContent().length > 0" class="preview-content">
              <scroll-view scroll-y="true" class="scrollable-content" :show-scrollbar="true" enhanced :bounce="true">
                <view class="expense-content-text">{{ getRiskPromptContent() }}</view>
                <view class="bottom-spacer"></view>
              </scroll-view>
            </view>
            <!-- 无内容时的提示 -->
            <view v-else class="no-content">
              <text class="preview-hint">点击进入内页编辑</text>
            </view>
          </view>
        </view>
      </view>
      <!-- 自定义预定须知列表（可编辑） -->
      <template v-if="dataReady">
        <view v-for="(notice, index) in getCustomNoticesList()" :key="(notice.id || index)" class="card">
          <view class="card-header expandable" @click="toggleSection(`customNotice_${index}`)">
            <view class="expense-header-content">
              <text class="expense-title-text">{{ notice.title }}</text>
              <view class="expense-status-container">
                <view class="expense-status-label-completed">
                  已填写
                </view>
                <view class="delete-btn" @click.stop="deleteCustomNotice(index)" v-if="!isBusinessType3">
                  <text class="delete-text">删除</text>
                </view>
              </view>
            </view>
          </view>
          <view class="card-body">
            <!-- 自定义预定须知预览区域 - 添加青色虚线框表示可编辑态，16:9固定比例 -->
            <view class="editable-preview-box aspect-ratio-16-9" :class="{ 'disabled': isBusinessType3 }"
              @click="handleCustomNoticeClick(notice, index)">
              <!-- 有内容时的预览 -->
              <view v-if="notice.alt && notice.alt.trim().length > 0" class="preview-content">
                <scroll-view scroll-y="true" class="scrollable-content" :show-scrollbar="true" enhanced :bounce="true">
                  <view class="expense-content-text">{{ notice.alt }}</view>
                  <view class="bottom-spacer"></view>
                </scroll-view>
              </view>
              <!-- 无内容时的提示 -->
              <view v-else class="no-content">
                <text class="preview-hint">点击进入内页编辑</text>
              </view>
            </view>
          </view>
        </view>
      </template>

      <view class="card" style="display: flex;" @click.stop="goToCustomNotice">
        <view class="btn-custom">

          <view class="add-icon-container">
            <text class="add-icon">+</text>
          </view>
          <text class="btn-text">新增自定义须知</text>
        </view>
      </view>
    </scroll-view>


    <!-- 底部按钮 -->
    <view class="bottom-btns">
      <button class="btn preview" :disabled="isSaving || isUploading" @click="onPreview">预览</button>
      <button class="btn draft" :class="{ 'draft-disabled': isApplyCall }" :disabled="isApplyCall"
        @click="saveDraft">存草稿</button>
      <button class="btn"
        :class="{ 'next': !isFormValid || !hasReadAgreement || isSaving || isUploading, 'next-active': isFormValid && hasReadAgreement && !isSaving && !isUploading }"
        :disabled="!isFormValid || !hasReadAgreement || isSaving || isUploading" @click="onNext">下一步</button>
    </view>
    <!-- 协议弹窗组件 -->
    <u-popup ref="agreementPopup" mode="bottom" :show="showPopup" @close="onPopupClose" :mask-close-able="false">
      <view class="popup-container">
        <view class="popup-header">
          <text class="popup-title">{{ popupTitle }}</text>
          <u-icon name="close" size="28" @click="closeAgreementPopup"></u-icon>
        </view>
        <scroll-view class="popup-content" scroll-y>
          <view v-if="currentAgreement === '参营协议'" class="agreement-content">
            <text class="agreement-title">参营协议</text>
            <text class="agreement-date">更新日期：2025年09月03日 生效日期：2025年09月03日</text>
            <text class="agreement-paragraph">一、 协议目的
              本参营协议（以下简称"协议"）由参营方（以下简称"参营方"）与得炼（郑州）信息科技有限公司（以下简称"平台方"）共同签署，旨在规范参营方在纤程旅行平台上开展研学旅行相关活动的行为，保障平台方、参营方及用户的权益利。</text>
            <text class="agreement-paragraph">二、 参营方资质与义务
              2.1 参营方应具备开展研学旅行活动的合法资质，包括但不限于LICENSE、相关许可证等，并确保所提供的所有资质文件真实、有效。
              2.2 参营方应遵守国家相关法律法规，严格按照研学旅行活动的相关标准和要求组织开展活动，确保活动的安全性和教育性。</text>
            <text class="agreement-paragraph">三、 平台方权利与义务
              3.1 平台方有权对参营方的资质、活动内容、服务质量等进行审核和监督。
              3.2 平台方应向参营方提供必要的技术支持和服务，协助参营方开展研学旅行活动。</text>
            <text class="agreement-paragraph">四、 费用与结算
              4.1 参营方应按照平台方的规定支付相关费用，包括但不限于平台服务费、推广费等。
              4.2 平台方应按照双方约定的时间和方式与参营方进行结算。</text>
            <text class="agreement-paragraph">五、 协议变更与终止
              5.1 本协议的变更或补充应经双方协商一致，并以书面形式确认。
              5.2 如参营方违反本协议的约定，平台方有权单方面终止本协议，并追究参营方的违约责任。</text>
            <text class="agreement-paragraph">六、 争议解决
              6.1 本协议的订立、效力、解释、履行和争议的解决，均适用中华人民共和国大陆地区法律。
              6.2 因本协议引起的任何争议，双方应首先友好协商解决；协商不成的，任何一方均有权将争议提交至平台所在地（河南省郑州市）有管辖权的人民法院诉讼解决。</text>
          </view>
          <view v-else-if="currentAgreement === '接团协议'" class="agreement-content">
            <text class="agreement-title">接团协议</text>
            <text class="agreement-date">更新日期：2025年09月03日 生效日期：2025年09月03日</text>
            <text class="agreement-paragraph">一、 协议目的
              本接团协议（以下简称"协议"）由接团方（以下简称"接团方"）与得炼（郑州）信息科技有限公司（以下简称"平台方"）共同签署，旨在规范接团方在纤程旅行平台上接收研学旅行团队的行为，保障平台方、接团方及用户的权益利。</text>
            <text class="agreement-paragraph">二、 接团方资质与义务
              2.1 接团方应具备接收研学旅行团队的合法资质，包括但不限于旅行社业务经营许可证、相关专业人员资质等，并确保所提供的所有资质文件真实、有效。
              2.2 接团方应按照平台方的要求和标准，为研学旅行团队提供优质的接待服务，包括但不限于交通、住宿、餐饮、导游等服务。</text>
            <text class="agreement-paragraph">三、 平台方权利与义务
              3.1 平台方有权对接团方的资质、服务质量等进行审核和监督。
              3.2 平台方应向接团方提供必要的技术支持和服务，协助接团方开展接团业务。</text>
            <text class="agreement-paragraph">四、 费用与结算
              4.1 接团方应按照平台方的规定收取相关费用，并向平台方支付一定比例的服务费。
              4.2 平台方应按照双方约定的时间和方式与接团方进行结算。</text>
            <text class="agreement-paragraph">五、 协议变更与终止
              5.1 本协议的变更或补充应经双方协商一致，并以书面形式确认。
              5.2 如接团方违反本协议的约定，平台方有权单方面终止本协议，并追究接团方的违约责任。</text>
            <text class="agreement-paragraph">六、 争议解决
              6.1 本协议的订立、效力、解释、履行和争议的解决，均适用中华人民共和国大陆地区法律。
              6.2 因本协议引起的任何争议，双方应首先友好协商解决；协商不成的，任何一方均有权将争议提交至平台所在地（河南省郑州市）有管辖权的人民法院诉讼解决。</text>
          </view>
          <view v-else-if="currentAgreement === '随队协议'" class="agreement-content">
            <text class="agreement-title">随队协议</text>
            <text class="agreement-date">更新日期：2025年09月03日 生效日期：2025年09月03日</text>
            <text class="agreement-paragraph">一、 协议目的
              本随队协议（以下简称"协议"）由随队方（以下简称"随队方"）与得炼（郑州）信息科技有限公司（以下简称"平台方"）共同签署，旨在规范随队方在纤程旅行平台上提供随队服务的行为，保障平台方、随队方及用户的权益利。</text>
            <text class="agreement-paragraph">二、 随队方资质与义务
              2.1 随队方应具备提供随队服务的合法资质和专业能力，包括但不限于相关专业证书、急救证书等，并确保所提供的所有资质文件真实、有效。
              2.2 随队方应按照平台方的要求和标准，为研学旅行团队提供优质的随队服务，包括但不限于安全保障、教育指导、生活照料等服务。</text>
            <text class="agreement-paragraph">三、 平台方权利与义务
              3.1 平台方有权对随队方的资质、服务质量等进行审核和监督。
              3.2 平台方应向随队方提供必要的技术支持和服务，协助随队方开展随队业务。</text>
            <text class="agreement-paragraph">四、 费用与结算
              4.1 随队方应按照平台方的规定收取相关费用，并向平台方支付一定比例的服务费。
              4.2 平台方应按照双方约定的时间和方式与随队方进行结算。</text>
            <text class="agreement-paragraph">五、 协议变更与终止
              5.1 本协议的变更或补充应经双方协商一致，并以书面形式确认。
              5.2 如随队方违反本协议的约定，平台方有权单方面终止本协议，并追究随队方的违约责任。</text>
            <text class="agreement-paragraph">六、 争议解决
              6.1 本协议的订立、效力、解释、履行和争议的解决，均适用中华人民共和国大陆地区法律。
              6.2 因本协议引起的任何争议，双方应首先友好协商解决；协商不成的，任何一方均有权将争议提交至平台所在地（河南省郑州市）有管辖权的人民法院诉讼解决。</text>
          </view>
        </scroll-view>
        <view class="popup-footer">
          <button class="popup-btn" type="primary" @click="closeAgreementPopup">我知道了</button>
        </view>
      </view>
    </u-popup>

    <!-- 图片裁剪弹窗 -->
    <u-popup :show="showCropPopup" mode="center" @close="cancelCrop" :closeOnClickOverlay="false">
      <view class="crop-modal">
        <view class="crop-header">
          <text class="crop-title">裁剪图片</text>
          <text class="crop-close" @click="cancelCrop">×</text>
        </view>

        <view class="crop-content">
          <!-- 裁剪预览区域 -->
          <view class="crop-preview-container">
            <view class="crop-canvas-wrapper">
              <canvas canvas-id="cropPreviewCanvas" id="cropPreviewCanvas" class="crop-preview-canvas"
                :style="{ width: canvasSize.width + 'px', height: canvasSize.height + 'px' }"
                @touchstart="onCropTouchStart" @touchmove="onCropTouchMove" @touchend="onCropTouchEnd"></canvas>

              <!-- 固定裁剪框 -->
              <view class="crop-frame" :style="{
                left: cropFrame.x + 'px',
                top: cropFrame.y + 'px',
                width: cropFrame.width + 'px',
                height: cropFrame.height + 'px'
              }"></view>
            </view>

            <!-- 裁剪提示 -->
            <view class="crop-tip-container">
              <text class="crop-tip">单指拖拽调整位置，双指捏合缩放</text>
            </view>
          </view>

          <!-- 隐藏的canvas用于最终裁剪 -->
          <canvas canvas-id="cropCanvas" id="cropCanvas"
            style="width: 800px; height: 800px; position: absolute; left: -9999px; top: -9999px;"></canvas>
        </view>

        <view class="crop-footer">
          <view class="crop-btn cancel-btn" @click="cancelCrop">取消</view>
          <view class="crop-btn confirm-btn" @click="confirmCrop">确认裁剪</view>
        </view>
      </view>
    </u-popup>

    <!-- 非阻塞进度提示 -->
    <view v-if="showProgressBar" class="progress-overlay">
      <view class="progress-container">
        <view class="progress-text">{{ progressText }}</view>
        <view class="progress-bar">
          <view class="progress-fill" :style="{ width: progressPercentage + '%' }"></view>
        </view>
        <view class="progress-percentage">{{ progressPercentage }}%</view>
      </view>
    </view>

    <!-- 非阻塞进度提示 -->
    <view v-if="showProgressBar" class="progress-overlay">
      <view class="progress-container">
        <view class="progress-text">{{ progressText }}</view>
        <view class="progress-bar">
          <view class="progress-fill" :style="{ width: progressPercentage + '%' }"></view>
        </view>
        <view class="progress-percentage">{{ progressPercentage }}%</view>
      </view>
    </view>
    <!-- 成团条款设置弹窗 -->
    <view v-if="showGroupRulesModalFlag" class="modal-overlay" @click="hideGroupRulesModal">
      <view class="modal-content" @click.stop>
        <view class="modal-header">
          <text class="modal-title">成团/退团/违约条款设置</text>
          <view class="modal-close" @click="hideGroupRulesModal">×</view>
        </view>

        <view class="modal-body">
          <!-- 模式选择 -->
          <view class="form-section">
            <view class="form-label">选择条款模式</view>
            <view class="mode-options">
              <view v-for="option in groupRulesModeOptions" :key="option.value" class="mode-option"
                :class="{ active: groupRulesData.mode === option.value }" @click="selectGroupRulesMode(option.value)">
                <text>{{ option.text }}</text>
              </view>
            </view>
          </view>

          <!-- 动态参数设置 -->
          <view v-if="groupRulesData.mode" class="form-section">
            <view class="form-label">参数设置</view>

            <view class="number-input-group">
              <view class="number-input-group__label">{{ groupRulesLabelTexts.leastNumberOfPeople }}</view>
              <view class="number-input-group__control">
                <view class="number-input-group__buttons">
                  <view class="number-input-decrement" :class="{ disabled: isGroupRuleMin('leastNumberOfPeople') }"
                    @tap="adjustGroupRulesNumber('leastNumberOfPeople', -1)">-</view>
                  <input class="number-input-group__input" type="number"
                    v-model.number="groupRulesData.leastNumberOfPeople" placeholder="请输入最低成团人数"
                    @input="handleGroupRulesInput('leastNumberOfPeople', $event)"
                    @change="handleGroupRulesChange('leastNumberOfPeople', $event)"
                    @blur="handleGroupRulesBlur('leastNumberOfPeople')" />
                  <view class="number-input-increment" :class="{ disabled: isGroupRuleMax('leastNumberOfPeople') }"
                    @tap="adjustGroupRulesNumber('leastNumberOfPeople', 1)">+</view>
                </view>
                <text class="number-input-group__unit">人</text>
              </view>
            </view>

            <view class="number-input-group">
              <view class="number-input-group__label">{{ groupRulesLabelTexts.fullNumberOfPeople }}</view>
              <view class="number-input-group__control">
                <view class="number-input-group__buttons">
                  <view class="number-input-decrement" :class="{ disabled: isGroupRuleMin('fullNumberOfPeople') }"
                    @tap="adjustGroupRulesNumber('fullNumberOfPeople', -1)">-</view>
                  <input class="number-input-group__input" type="number"
                    v-model.number="groupRulesData.fullNumberOfPeople" placeholder="请输入最多满员人数"
                    @input="handleGroupRulesInput('fullNumberOfPeople', $event)"
                    @change="handleGroupRulesChange('fullNumberOfPeople', $event)"
                    @blur="handleGroupRulesBlur('fullNumberOfPeople')" />
                  <view class="number-input-increment" :class="{ disabled: isGroupRuleMax('fullNumberOfPeople') }"
                    @tap="adjustGroupRulesNumber('fullNumberOfPeople', 1)">+</view>
                </view>
                <text class="number-input-group__unit">人</text>
              </view>
            </view>

            <view class="number-input-group number-input-group--confirmation">
              <view class="number-input-group__label number-input-group__label--multiline">
                <text class="number-input-group__label-line">{{ groupRulesLabelTexts.confirmationHeading }}</text>
                <text class="number-input-group__label-line number-input-group__label-line--sub">{{
                  groupRulesLabelTexts.confirmationSub }}</text>
              </view>
              <view class="number-input-group__control number-input-group__control--confirmation">
                <view class="number-input-group__buttons">
                  <view class="number-input-decrement" :class="{ disabled: isGroupRuleMin('confirmationTime') }"
                    @tap="adjustGroupRulesNumber('confirmationTime', -1)">-</view>
                  <input class="number-input-group__input" type="number"
                    v-model.number="groupRulesData.confirmationTime" placeholder="请输入天数"
                    @input="handleGroupRulesInput('confirmationTime', $event)"
                    @change="handleGroupRulesChange('confirmationTime', $event)"
                    @blur="handleGroupRulesBlur('confirmationTime')" />
                  <view class="number-input-increment" :class="{ disabled: isGroupRuleMax('confirmationTime') }"
                    @tap="adjustGroupRulesNumber('confirmationTime', 1)">+</view>
                </view>
                <text class="number-input-group__unit">天</text>
              </view>
            </view>

            <!-- 退团政策设置 -->
            <view class="form-section">
              <view class="form-label">
                退团政策设置
                <view class="add-policy-btn" @click="addPolicy">
                  <text>+ 添加政策</text>
                </view>
              </view>
              <view v-for="(policy, index) in groupRulesData.policies" :key="index" class="policy-row">
                <view class="policy-item">
                  <text class="policy-label">退团时间</text>
                  <input class="form-input" v-model="policy.days" placeholder="如：15日及以上" />
                </view>
                <view class="policy-item">
                  <text class="policy-label">违约金比例(%)</text>
                  <input class="form-input" type="number" v-model.number="policy.percentage" placeholder="如：5" />
                </view>
                <view class="delete-policy-btn" @click="deletePolicy(index)" v-if="groupRulesData.policies.length > 1">
                  <text>删除</text>
                </view>
              </view>
            </view>
          </view>
        </view>

        <view class="modal-footer">
          <view class="modal-button cancel" @click="hideGroupRulesModal">取消</view>
          <view class="modal-button confirm" @click="saveGroupRules">保存</view>
        </view>
      </view>
    </view>
  </view>
</template>

<script>
import GlobalTopNav from '@/components/GlobalTopNav.vue';
import { request, BASE_URL } from '@/utils/request.js';
import { useProductStore } from '@/store/product.js';

const GROUP_RULES_CONSTANTS = {
  MIN_APPLICATION_DEADLINE: 7,
  MAX_APPLICATION_DEADLINE: 60,
  MIN_CONFIRMATION_TIME: 3,
  MAX_CONFIRMATION_TIME: 30,
  MIN_PEOPLE: 10,
  MAX_PEOPLE: 50,
  MIN_GROUP_PEOPLE: 10,
  MAX_GROUP_PEOPLE: 30
};

const GROUP_RULES_FIELD_LIMITS = {
  leastNumberOfPeople: {
    min: GROUP_RULES_CONSTANTS.MIN_GROUP_PEOPLE,
    max: GROUP_RULES_CONSTANTS.MAX_GROUP_PEOPLE
  },
  fullNumberOfPeople: {
    min: GROUP_RULES_CONSTANTS.MIN_PEOPLE,
    max: GROUP_RULES_CONSTANTS.MAX_PEOPLE
  },
  applicationDeadline: {
    min: GROUP_RULES_CONSTANTS.MIN_APPLICATION_DEADLINE,
    max: GROUP_RULES_CONSTANTS.MAX_APPLICATION_DEADLINE
  },
  confirmationTime: {
    min: GROUP_RULES_CONSTANTS.MIN_CONFIRMATION_TIME,
    max: GROUP_RULES_CONSTANTS.MAX_CONFIRMATION_TIME
  }
};

const GROUP_RULES_FIELD_LABELS = {
  leastNumberOfPeople: '最低成团人数',
  fullNumberOfPeople: '最多满员人数',
  applicationDeadline: '申请截止天数',
  confirmationTime: '成团确认天数'
};

const GROUP_RULES_FIELD_UNITS = {
  leastNumberOfPeople: '人',
  fullNumberOfPeople: '人',
  applicationDeadline: '天',
  confirmationTime: '天'
};
export default {
  components: {
    GlobalTopNav
  },
  data() {
    return {
      productType: '', //产品类型
      storeIsFormValid: true,
      dataReady: false,
      // noticesRenderKey: 0,
      // 弹窗相关状态
      showPopup: false,
      companyInfo: {},
      businessEntry: '', // 入口标识
      popupTitle: '',
      currentAgreement: '',

      // 进度条相关状态
      showProgressBar: false,
      progressText: '',
      progressPercentage: 0,

      // 图片裁剪相关状态
      showCropPopup: false,
      cropImagePath: '',
      cropResolve: null,
      cropReject: null,

      // 裁剪框相关状态（固定位置）
      cropFrame: {
        x: 50,
        y: 50,
        width: 200,
        height: 200
      },
      // 图片位置和缩放状态
      imagePosition: {
        x: 0,
        y: 0,
        scale: 1
      },
      isDragging: false,
      dragStartPos: { x: 0, y: 0 },
      originalImagePos: { x: 0, y: 0, scale: 1 },
      canvasSize: { width: 300, height: 200 },
      imageInfo: { width: 0, height: 0 },

      maxImages: 8,
      categoryOptions: [
        {
          id: 1,
          active: 0,
          text: "亲近自然",
          child: [
            { id: 1001, text: "森林探秘", active: 0 },
            { id: 1002, text: "农耕体验", active: 0 },
            { id: 1003, text: "捕捞采摘", active: 0 },
            { id: 1004, text: "营地生活", active: 0 },
            { id: 1005, text: "观鸟研学", active: 0 },
            { id: 1006, text: "星空观测", active: 0 },
            { id: 1007, text: "昆虫观察", active: 0 },
            { id: 1008, text: "湿地探索", active: 0 },
            { id: 1009, text: "溯溪探险", active: 0 },
            { id: 1010, text: "美景写生", active: 0 },
            { id: 1011, text: "露营体验", active: 0 },
            { id: 1012, text: "植物科考", active: 0 },
            { id: 1013, text: "自然科普", active: 0 }
          ]
        },
        {
          id: 2,
          active: 0,
          text: "户外活动",
          child: [
            { id: 2001, text: "徒步", active: 0 },
            { id: 2002, text: "骑行", active: 0 },
            { id: 2003, text: "攀岩", active: 0 },
            { id: 2004, text: "漂流", active: 0 },
            { id: 2005, text: "滑雪", active: 0 },
            { id: 2006, text: "皮划艇/桨板", active: 0 },
            { id: 2007, text: "速降", active: 0 },
            { id: 2008, text: "励志拓展", active: 0 },
            { id: 2009, text: "野外技能", active: 0 },
            { id: 2010, text: "生存训练", active: 0 },
            { id: 2011, text: "障碍赛", active: 0 }
          ]
        },
        {
          id: 3,
          active: 0,
          text: "科学探索",
          child: [
            { id: 3001, text: "航空相关", active: 0 },
            { id: 3002, text: "人工智能", active: 0 },
            { id: 3003, text: "名校探访", active: 0 },
            { id: 3004, text: "天文科学", active: 0 },
            { id: 3005, text: "职业启蒙", active: 0 },
            { id: 3006, text: "地质考察", active: 0 },
            { id: 3007, text: "工厂/名企探访", active: 0 }
          ]
        },
        {
          id: 4,
          active: 0,
          text: "文化传承",
          child: [
            { id: 4001, text: "劳动实践", active: 0 },
            { id: 4002, text: "红色研学", active: 0 },
            { id: 4003, text: "非遗传承", active: 0 },
            { id: 4004, text: "民俗文化", active: 0 }
          ]
        },
        {
          id: 5,
          active: 0,
          text: "博物历史",
          child: [
            { id: 5001, text: "生物化石", active: 0 },
            { id: 5002, text: "考古模拟", active: 0 },
            { id: 5003, text: "自然博物", active: 0 },
            { id: 5004, text: "遗址探秘", active: 0 }
          ]
        },
        {
          id: 6,
          active: 0,
          text: "国际视野",
          child: [
            { id: 6001, text: "全真插班", active: 0 },
            { id: 6002, text: "名校参访", active: 0 },
            { id: 6003, text: "国际体验", active: 0 },
            { id: 6004, text: "全球生态", active: 0 }
          ]
        }
      ],
      isCustomCategoryMode: false,
      customCategoryOptionText: '自定义',
      productStore: {},
      expenses: '',
      //编辑标记
      productId: '',
      isEditMode: false,
      isApplyCall: false, // 申请调用模式标记
      sourcePeriodId: '', // 源班期ID
      sourceMerchantId: '', // 源商家ID
      sourceMerchantType: 1, // 源商家类型（默认为1-地接社/场馆）
      sourceSkuId: '', // 源SKU ID
      applyCallPrice: 0, // 申请调用时的价格
      initialImageCount: 0, // 申请调用模式下的初始图片数量

      form: {
        images: [],
        title: "",
        name: "",
        minYearOld: 3,
        maxYearOld: 18,
        parentsAccompanied: false,
        // 用于二级分类选择的临时变量
        currentMainCategoryIndex: -1,
        currentSubCategoryIndex: -1,
        customMainCategory: '',
        customSubCategory: ''
      },
      ageRange: [3, 18],
      ageMinLimit: 3, // 年龄限制最小值
      ageMaxLimit: 18, // 年龄限制最大值
      isSaving: false,
      isUploading: false,
      hasReadAgreement: true,
      expanded: {
        feature: true,
        featureCustom: true,
        itinerary: true,
        included: true,
        excluded: true,  // 默认全部展开
        other: true,   // 默认全部展开
        riskPrompt: true,
        customNotices: true,
        groupRules: true,
        guaranteePrompt: true,   // 默认全部展开
        bookingInfo: true
      },
      groupRulesSyncing: false,
      groupRulesClampFlags: {},
      // 成团/退团/违约条款相关数据
      groupRulesData: {
        mode: '', // 选择的模式：'province_1_2', 'province_3_plus', 'cross_province_3_plus'
        modeText: '', // 模式显示文本
        leastNumberOfPeople: 24, // 最低成团人数
        fullNumberOfPeople: 45, // 最多满员人数
        confirmDays: '3-7', // 行程前确认天数（字符串形式，用于兼容旧逻辑）
        applicationDeadline: 7, // 行程前确认天数下限
        confirmationTime: 15, // 行程前确认天数上限
        policies: [] // 退团政策数组
      },
      // 成团条款弹窗相关
      showGroupRulesModalFlag: false,
      groupRulesModeOptions: [
        { value: 'province_1_2', text: '省内 1-2日营' },
        { value: 'province_3_plus', text: '省内 3日及以上' },
        { value: 'cross_province_3_plus', text: '跨省/市 3日及以上' }
      ],
      // 图片加载错误标记数组
      imageErrors: [],
      // 模拟数据
      productFeatures: [
        {
          type: 'text',
          alt: '探索自然奥秘',
          order: 1
        }
      ],
      // 产品特色相关
      isAddingText: false,
      featureTextInput: '',
      isUploadingFeature: false
    };
  },
  computed: {
    // 判断是否为三类商户（教培）- 只允许查看，禁止编辑输入框和选择框
    isBusinessType3() {
      return this.companyInfo && this.companyInfo.businessType === 3;
    },

    // 判断是否禁用标题输入框
    disableTitleInput() {
      // 三类商家禁用
      if (this.isBusinessType3) return true;
      // 其他情况（包括显示"泛营地接待"时）都可以编辑
      return false;
    },

    disableCategorySelection() {
      const businessType = this.companyInfo && this.companyInfo.businessType;
      if (this.isBusinessType3) return true;
      if (this.isApplyCall && businessType === 2) return true;
      return false;
    },

    groupRulesBusinessType() {
      return this.companyInfo && this.companyInfo.businessType ? this.companyInfo.businessType : 0;
    },

    groupRulesLabelTexts() {
      return {
        leastNumberOfPeople: '最低成团人数',
        fullNumberOfPeople: '每团满员人数',
        confirmationHeading: '成团确认日期：',
        confirmationSub: '行程日前'
      };
    },

    groupRulesShowConfirmationRange() {
      return this.groupRulesBusinessType === 1;
    },

    groupRulesConfirmationRangeDisplay() {
      if (!this.groupRulesShowConfirmationRange) {
        return '';
      }
      const value = this.groupRulesData && this.groupRulesData.applicationDeadline;
      if (value === '' || value === null || value === undefined) {
        return '';
      }
      return `${value}~`;
    },

    // 判断是否禁用产品特色编辑（二类商家-组团业务 或 三类商家 或 申请调用模式）
    shouldDisableFeature() {
      if (this.isBusinessType3) return true;
      // 申请调用模式：与二类商家-组团业务一致，需要显示产品特色自定义
      if (this.isApplyCall) return true;
      const businessType = this.companyInfo && this.companyInfo.businessType;
      if (businessType === 2) {
        const productTypeNum = Number(this.productType);
        if (productTypeNum === 4) return true; // 组团业务
      }
      return false;
    },
    // 按order排序的productFeatures
    sortedProductFeatures() {
      if (!this.productStore?.subPageData?.productFeatures || !Array.isArray(this.productStore.subPageData.productFeatures)) {
        return [];
      }
      return [...this.productStore.subPageData.productFeatures].sort((a, b) => {
        const orderA = a.order !== undefined ? a.order : 999;
        const orderB = b.order !== undefined ? b.order : 999;
        return orderA - orderB;
      });
    },

    normalizedProductFeaturesHtml() {
      if (!this.productStore?.subPageData?.productFeaturesHtml) {
        return '';
      }
      return this.normalizeFeatureHtml(this.productStore.subPageData.productFeaturesHtml);
    },
    // 按order排序的productFeaturesCustom
    sortedProductFeaturesCustom() {
      if (!this.productStore?.subPageData?.productFeaturesCustom || !Array.isArray(this.productStore.subPageData.productFeaturesCustom)) {
        return [];
      }
      return [...this.productStore.subPageData.productFeaturesCustom].sort((a, b) => {
        const orderA = a.order !== undefined ? a.order : 999;
        const orderB = b.order !== undefined ? b.order : 999;
        return orderA - orderB;
      });
    },
    normalizedProductFeaturesCustomHtml() {
      if (!this.productStore?.subPageData?.productFeaturesCustomHtml) {
        return '';
      }
      return this.normalizeFeatureHtml(this.productStore.subPageData.productFeaturesCustomHtml);
    },

    typeDisplayName() {
      const map = {
        '1': '泛营地接待',
        '2': '综合研学业务',
        '3': '目的地接待',
        '4': '组团业务',
        '5': '研学活动'
      };

      const productTypeKey = this.productType ? String(this.productType) : '';
      if (productTypeKey && map[productTypeKey]) {
        return map[productTypeKey];
      }

      // 根据 businessEntry 兜底映射
      if (this.businessEntry) {
        if (this.businessEntry.includes('dest_receive') || this.businessEntry.includes('目的地接待')) {
          return map['3'];
        }
        if (this.businessEntry.includes('comprehensive') || this.businessEntry.includes('综合研学')) {
          return map['2'];
        }
        if (this.businessEntry.includes('group') || this.businessEntry.includes('组团')) {
          return map['4'];
        }
        if (this.businessEntry.includes('third_research') || this.businessEntry.includes('研学活动')) {
          return map['5'];
        }
        if (this.businessEntry.includes('yanxue') || this.businessEntry.includes('研学')) {
          return map['1'];
        }
      }

      return '产品';
    },

    pageTitle() {
      const productTypeKey = this.productType ? String(this.productType) : '';
      const businessType = this.companyInfo && this.companyInfo.businessType;

      // 🔑 特殊情况：通过组团业务调用目的地接待产品时，显示"组团业务"
      const businessEntry = uni.getStorageSync('businessEntry') || '';
      if (productTypeKey === '3' && businessType === 2 && this.isApplyCall && businessEntry === 'group_travel') {
        return '组团业务';
      }

      // 特殊情况：当 productType = 1 且 businessType = 2 时，显示"创建组团业务"
      if (productTypeKey === '1' && businessType === 2) {
        return '创建组团业务';
      }

      // 根据 productType 返回对应的标题（已移除"创建"和"产品"字样）
      const titleMap = {
        '1': '泛营地接待',
        '2': '综合研学业务',
        '3': '目的地接待',
        '4': '组团业务',
        '5': '研学活动' // 虽然用户没有提到，但保留以防需要
      };

      if (productTypeKey && titleMap[productTypeKey]) {
        return titleMap[productTypeKey];
      }

      // 兜底：如果没有匹配的 productType，返回默认标题
      return '产品';
    },

    // 根据商家类型和产品类型返回需要显示的协议列表
    getAgreements() {
      const agreements = []
      
      // 申请调用模式：与二类商家-组团业务一致
      if (this.isApplyCall) {
        agreements.push('境内研学接团协议')
        agreements.push('境内研学参营协议')
        agreements.push('境内研学随队协议')
        return agreements
      }
      
      const businessType = this.companyInfo && this.companyInfo.businessType

      // 根据 businessEntry 或 productType 确定实际的产品类型
      let actualProductType = this.productType

      // 如果 productType 是数字，映射为字符串类型
      if (typeof actualProductType === 'number' || (typeof actualProductType === 'string' && /^\d+$/.test(actualProductType))) {
        const productTypeNum = Number(actualProductType)
        // 1-泛营地接待, 2-综合研学业务, 3-目的地接待, 4-组团业务, 5-研学活动
        if (productTypeNum === 1 || productTypeNum === 3) {
          actualProductType = 'dest_receive'
        } else if (productTypeNum === 2) {
          actualProductType = 'comprehensive_study'
        } else if (productTypeNum === 4) {
          actualProductType = 'group_travel'
        } else if (productTypeNum === 5) {
          actualProductType = 'yanxue'
        }
      }

      // 如果没有 productType，根据 businessEntry 推断
      if (!actualProductType && this.businessEntry) {
        if (this.businessEntry === 'dest_receive' || this.businessEntry === '目的地接待业务' || this.businessEntry?.includes('目的地接待') || this.businessEntry?.includes('接待')) {
          actualProductType = 'dest_receive'
        } else if (this.businessEntry === 'comprehensive_study' || this.businessEntry === 'comprehensive_research' || this.businessEntry?.includes('综合研学')) {
          actualProductType = 'comprehensive_study'
        } else if (this.businessEntry === 'group_travel' || this.businessEntry?.includes('组团')) {
          actualProductType = 'group_travel'
        } else if (this.businessEntry === 'yanxue' || this.businessEntry?.includes('研学')) {
          actualProductType = 'yanxue'
        }
      }

      // 一类商家 - 泛营地接待
      if (businessType === 1) {
        agreements.push('境内研学接团协议')
      }
      // 二类商家
      else if (businessType === 2) {
        // 目的地接待
        if (actualProductType === 'dest_receive') {
          agreements.push('境内研学接团协议')
        }
        // 综合研学业务
        else if (actualProductType === 'comprehensive_study') {
          agreements.push('境内研学参营协议')
          agreements.push('境内研学随队协议')
        }
        // 组团业务
        else if (actualProductType === 'group_travel') {
          agreements.push('境内研学接团协议')
          agreements.push('境内研学参营协议')
          agreements.push('境内研学随队协议')
        }
      }
      // 三类商家 - 研学活动
      else if (businessType === 3) {
        agreements.push('境内研学随队协议')
        agreements.push('境内研学参营协议')
      }

      return agreements
    },

    // 判断是否显示成团/退团/违约条款
    // 二类商家（businessType=2）在从"目的地接待业务"入口进来时不显示
    shouldShowGroupRules() {
      const businessType = this.companyInfo && this.companyInfo.businessType;
      const isBusinessType2 = businessType === 2;
      const isBusinessType3 = businessType === 3;
      // 目的地接待业务的入口标识：dest_receive
      const isDestinationReception = this.businessEntry === 'dest_receive'
        || this.businessEntry === '目的地接待业务'
        || this.businessEntry === 'destination_receive'
        || this.businessEntry === 'destinationReceive'
        || this.businessEntry?.includes('目的地接待')
        || this.businessEntry?.includes('接待');



      // 三类商家：直接显示
      if (isBusinessType3) {
        return true;
      }
      // 二类商家：目的地接待入口不显示，其余显示
      if (!isBusinessType2) {
        return false;
      }
      // 如果入口标识是"目的地接待业务"（dest_receive），则不显示
      if (isDestinationReception) {
        //('是目的地接待业务入口，不显示成团条款');
        return false;
      }
      //('显示成团条款');
      return true;
    },

    // 根据用户类型和编辑模式动态返回最大图片数量
    maxImages() {
      if (!this.companyInfo || !this.companyInfo.businessType) {
        return 8; // 默认8张
      }

      // businessType: 1-地接社/场馆，2-旅行社，3-教培
      if (this.companyInfo.businessType === 1) {
        return this.isEditMode ? 12 : 8; // 1类用户可上传1-8张
      } else {
        return this.isEditMode ? 12 : 8; // 其他用户暂时也保持1-8张，可根据需求调整
      }
    },

    // 判断是否可以继续上传图片
    canUploadMore() {
      // 如果不是申请调用模式，使用原有的逻辑
      if (!this.isApplyCall) {
        return this.form.images.length < this.maxImages;
      }

      // 申请调用模式下：最多只能上传 initialImageCount + 4 张图片
      const maxUploadCount = this.initialImageCount + 4;
      return this.form.images.length < maxUploadCount;
    },

    // 计算属性:产品特色预览
    featurePreview() {
      //('===== featurePreview 计算属性被调用 =====');
      //('productStore存在:', !!this.productStore);
      //('subPageData存在:', !!this.productStore?.subPageData);

      if (!this.productStore || !this.productStore.subPageData) {
        //('productStore或subPageData不存在,返回空字符串');
        return '';
      }

      //('productFeatures数据:', this.productStore.subPageData?.productFeatures);
      //('productFeatures长度:', this.productStore.subPageData?.productFeatures?.length);

      // 优先使用新的productFeatures数组数据
      if (this.productStore.subPageData?.productFeatures && this.productStore.subPageData.productFeatures.length > 0) {
        //('productFeatures有数据');

        const firstTextItem = this.productStore.subPageData.productFeatures.find(item => item.type === 'text' && item.alt);
        //('找到的第一个文本项:', firstTextItem);

        if (firstTextItem && firstTextItem.alt) {
          const preview = firstTextItem.alt.replace(/[\n\r]/g, ' ').trim();
          const result = preview.length > 50 ? preview.substring(0, 50) + '...' : preview;
          //('返回预览文本:', result);
          return result;
        }

        // 统计图片数量
        const imageCount = this.productStore.subPageData.productFeatures.filter(item => item.type === 'image' && item.imgUrl).length;
        if (imageCount > 0) {
          const result = `共${this.productStore.subPageData.productFeatures.length}个特色元素（${imageCount}张图片）`;
          //('返回图片统计:', result);
          return result;
        }

        const result = `共${this.productStore.subPageData.productFeatures.length}个特色元素`;
        //('返回元素统计:', result);
        return result;
      }

      //('productFeatures为空,尝试使用旧的feature数据');

      // 兼容旧的feature数据
      const content = this.productStore.subPageData.feature;
      if (!content || (typeof content === 'string' && content.trim() === '[]')) {
        //('旧的feature数据也为空');
        return '';
      }

      // 避免显示JSON字符串
      if (typeof content === 'string') {
        try {
          // 尝试解析内容，看看是否是JSON字符串
          const parsedContent = JSON.parse(content);
          if (Array.isArray(parsedContent)) {
            // 如果是数组，返回元素数量
            return `共${parsedContent.length}个特色元素`;
          }
        } catch (e) {
          // 如果不是JSON字符串，直接处理
        }

        // 处理普通字符串，避免显示纯数字
        const preview = content.replace(/[\n\r]/g, ' ').trim();
        // 检查是否是纯数字字符串，如果是则返回更友好的提示
        if (/^\d+$/.test(preview)) {
          return '包含数字类型的产品特色';
        }
        return preview.length > 15 ? preview.substring(0, 15) + '...' : preview;
      } else {
        // 对于非字符串类型，返回更友好的提示
        return '包含产品特色信息';
      }
    },



    // 计算属性：行程摘要数据
    itinerarySummary() {
      if (!this.productStore || !this.productStore.subPageData || !this.productStore.subPageData.specificItinerary || this.productStore.subPageData.specificItinerary.length === 0) {
        return [];
      }

      return this.productStore.subPageData.specificItinerary.map((dayItinerary, index) => {
        return {
          title: dayItinerary.title || dayItinerary.main || `第${index + 1}天行程`,
          items: dayItinerary.items || [],
          firstItem: dayItinerary.items && dayItinerary.items.length > 0 ? dayItinerary.items[0].title : ''
        };
      });
    },

    // 计算属性：费用包含预览
    expensesIncludedPreview() {
      try {
        //("费用包含预览")
        // 检查数据是否存在且格式正确
        if (this.productStore &&
          this.productStore.subPageData &&
          Array.isArray(this.productStore.subPageData.expensesIncluded) &&
          this.productStore.subPageData.expensesIncluded.length > 0 &&
          this.productStore.subPageData.expensesIncluded[0]) {

          const firstItem = this.productStore.subPageData.expensesIncluded[0];
          // 确保属性存在
          const firstContent = firstItem.alt || firstItem.content || '';
          if (firstItem.title && firstContent) {
            return firstItem.title + ': ' + (firstContent.length > 15 ? firstContent.substring(0, 15) + '...' : firstContent);
          }
        }
        return '';
      } catch (error) {
        console.error('计算费用包含预览出错:', error);
        return '';
      }
    },

    // 计算属性：费用不含预览
    expensesExcludedPreview() {
      try {
        //("费用不含预览")
        // 费用不含已改为字符串
        const content = this.productStore?.subPageData?.expensesExcluded || '';
        if (!content) return '';
        const preview = content.replace(/[\n\r]/g, ' ').trim();
        return preview.length > 15 ? preview.substring(0, 15) + '...' : preview;
      } catch (error) {
        console.error('计算费用不含预览出错:', error);
        return '';
      }
    },

    // 计算属性：风险提示预览
    riskPromptPreview() {

      //("风险提示预览")
      const content = this.productStore.subPageData.riskPrompt;
      if (!content) return '';
      const preview = content.replace(/[\n\r]/g, ' ').trim();
      return preview.length > 15 ? preview.substring(0, 15) + '...' : preview;
    },

    // 计算属性：保障提示预览
    guaranteePromptPreview() {
      const content = this.productStore.subPageData.guaranteePrompt;
      if (!content) return '';
      const preview = content.replace(/[\n\r]/g, ' ').trim();
      return preview.length > 15 ? preview.substring(0, 15) + '...' : preview;
    },

    // 计算属性：经营协议预览
    bookingInfoPreview() {

      const content = this.productStore.subPageData.bookingInfo;
      if (!content) return '';
      const preview = content.replace(/[\n\r]/g, ' ').trim();
      return preview.length > 15 ? preview.substring(0, 15) + '...' : preview;
    },
    // 计算属性：判断各模块内容状态
    moduleStatus() {
      return {
        // 产品特色状态
        feature: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData;
          return (data.productFeaturesHtml && data.productFeaturesHtml.trim()) ||
            (data.productFeatures && data.productFeatures.length > 0) ||
            (data.feature && data.feature.trim() && data.feature !== '[]');
        },
        // 产品特色自定义状态
        featureCustom: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData;
          return (data.productFeaturesCustomHtml && data.productFeaturesCustomHtml.trim()) ||
            (data.productFeaturesCustom && data.productFeaturesCustom.length > 0);
        },

        // 具体行程状态
        itinerary: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData;
          return data.specificItinerary && data.specificItinerary.length > 0;
        },

        // 费用包含状态
        included: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData;
          return data.expensesIncluded && data.expensesIncluded.length > 0;
        },

        // 费用不含状态
        excluded: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData.expensesExcluded;
          if (!data) return false;
          if (typeof data === 'string') {
            return data.trim() !== '';
          }
          return Array.isArray(data) && data.length > 0;
        },

        // 另付费项目状态
        other: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData.otherExpenses;
          if (!data) return false;
          if (typeof data === 'string') {
            return data.trim() !== '';
          }
          return Array.isArray(data) && data.length > 0;
        },

        // 风险提示状态
        riskPrompt: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData.riskPrompt;
          if (!data) return false;
          if (typeof data === 'string') {
            return data.trim() !== '';
          }
          return Array.isArray(data) && data.length > 0;
        },

        // 自定义预定须知状态
        customNotices: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData.customNotices;
          return data && Array.isArray(data) && data.length > 0;
        },

        // 成团条款状态
        groupRules: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData;
          return data.groupRulesData && data.groupRulesData.mode;
        },

        // 预订限制状态
        bookingInfo: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData;
          return data.bookingInfo && data.bookingInfo.trim() !== '';
        },

        // 保障提示状态
        guaranteePrompt: () => {
          if (!this.productStore?.subPageData) return false;
          const data = this.productStore.subPageData;
          if (!data.guaranteePrompt) return false;
          if (Array.isArray(data.guaranteePrompt)) {
            return data.guaranteePrompt.length > 0;
          }
          return data.guaranteePrompt.trim() !== '';
        }
      };
    },

    // 表单是否有效
    isFormValid() {
      // 为了方便测试，这里添加一个简单的判断
      // 实际项目中应该恢复原来的验证逻辑
      const hasTitle = this.form.title && this.form.title.trim().length >= 2;
      const hasCategory = !!this.form.title;
      const hasImage = this.form.images && this.form.images.length > 0;

      // 为了测试，我们可以暂时设置一个更简单的条件
      return hasTitle;
    },

    mainCategoryPickerOptions() {
      const options = this.categoryOptions.map(cat => cat.text);
      if (!options.includes(this.customCategoryOptionText)) {
        options.push(this.customCategoryOptionText);
      }
      return options;
    },

    mainCategoryPickerValue() {
      if (this.isCustomCategoryMode) {
        return this.categoryOptions.length;
      }
      if (this.form.currentMainCategoryIndex >= 0) {
        return this.form.currentMainCategoryIndex;
      }
      return 0;
    },

    selectedMainCategoryDisplay() {
      if (this.isCustomCategoryMode) {
        return this.form.customMainCategory || this.customCategoryOptionText;
      }
      return this.resolvedMainCategory?.text || '';
    },

    selectedSubCategoryDisplay() {
      if (this.isCustomCategoryMode) {
        return this.form.customSubCategory || '';
      }
      if (this.form.currentSubCategoryIndex >= 0) {
        return this.resolvedSubCategories[this.form.currentSubCategoryIndex]?.text || '';
      }
      return '';
    },

    hasStandardMainCategory() {
      return !this.isCustomCategoryMode && !!this.resolvedMainCategory;
    },

    currentSubCategories() {
      return this.isCustomCategoryMode ? [] : this.resolvedSubCategories;
    },

    resolvedMainCategory() {
      if (this.form.currentMainCategoryIndex < 0) return null;
      return this.categoryOptions[this.form.currentMainCategoryIndex] || null;
    },

    resolvedSubCategories() {
      return this.resolvedMainCategory?.child || [];
    },

    // 计算属性：行程前确认天数显示文本
    confirmDaysRangeDisplay() {
      const start = this.normalizeConfirmDayValue(this.groupRulesData.applicationDeadline);
      const end = this.normalizeConfirmDayValue(this.groupRulesData.confirmationTime);
      if (start !== '' && end !== '') {
        return `${start}-${end}`;
      }
      if (start !== '') {
        return `${start}`;
      }
      if (end !== '') {
        return `${end}`;
      }
      if (this.groupRulesData.confirmDays && typeof this.groupRulesData.confirmDays === 'string') {
        return this.groupRulesData.confirmDays;
      }
      return '3-7';
    },

    // 计算属性：监听store中的成团条款数据，便于watch兼容传值
    productStoreGroupRules() {
      if (!this.productStore || !this.productStore.subPageData) return null;
      return this.productStore.subPageData.groupRulesData || null;
    }
  },
  watch: {
    ageRange: {
      handler(newRange) {
        // 同步更新form中的minYearOld和maxYearOld
        this.form.minYearOld = newRange[0];
        this.form.maxYearOld = newRange[1];
      },
      deep: true
    },
    isCustomCategoryMode(val) {
      this.syncCategoryTitle();
    },
    'form.customMainCategory'(val) {
      if (this.isCustomCategoryMode) {
        this.syncCategoryTitle();
      }
    },
    'form.customSubCategory'(val) {
      if (this.isCustomCategoryMode) {
        this.syncCategoryTitle();
      }
    },
    'form.currentMainCategoryIndex'(val) {
      if (!this.isCustomCategoryMode) {
        this.syncCategoryTitle();
      }
    },
    'form.currentSubCategoryIndex'(val) {
      if (!this.isCustomCategoryMode) {
        this.syncCategoryTitle();
      }
    },
    'groupRulesData.leastNumberOfPeople'() {
      const boundsAdjusted = this.enforceGroupRulesBounds('leastNumberOfPeople');
      const relationAdjusted = this.enforceGroupRulesCrossRules('leastNumberOfPeople');
      this.setGroupRulesClampFlag('leastNumberOfPeople', boundsAdjusted || relationAdjusted);
    },
    'groupRulesData.fullNumberOfPeople'() {
      const boundsAdjusted = this.enforceGroupRulesBounds('fullNumberOfPeople');
      const relationAdjusted = this.enforceGroupRulesCrossRules('fullNumberOfPeople');
      this.setGroupRulesClampFlag('fullNumberOfPeople', boundsAdjusted || relationAdjusted);
    },
    'groupRulesData.applicationDeadline'() {
      const boundsAdjusted = this.enforceGroupRulesBounds('applicationDeadline');
      const relationAdjusted = this.enforceGroupRulesCrossRules('applicationDeadline');
      this.setGroupRulesClampFlag('applicationDeadline', boundsAdjusted || relationAdjusted);
      this.syncConfirmDaysString();
    },
    'groupRulesData.confirmationTime'() {
      const boundsAdjusted = this.enforceGroupRulesBounds('confirmationTime');
      const relationAdjusted = this.enforceGroupRulesCrossRules('confirmationTime');
      this.setGroupRulesClampFlag('confirmationTime', boundsAdjusted || relationAdjusted);
      this.syncConfirmDaysString();
    },
    groupRulesData: {
      deep: true,
      immediate: true,
      handler(newVal) {
        if (this.groupRulesSyncing) return;
        if (!this.productStore || !this.productStore.subPageData) return;
        this.productStore.subPageData.groupRulesData = {
          ...JSON.parse(JSON.stringify({
            mode: '',
            modeText: '',
            minPeople: '',
            maxPeople: '',
            confirmDays: '',
            applicationDeadline: '',
            confirmationTime: '',
            policies: []
          })),
          ...JSON.parse(JSON.stringify(newVal))
        };
      }
    },
    productStoreGroupRules: {
      deep: true,
      immediate: true,
      handler(val) {
        if (!val || typeof val !== 'object') return;
        this.groupRulesSyncing = true;
        this.applyGroupRulesFromSource(val);
        this.groupRulesSyncing = false;
      }
    }
  },
  onLoad(options) {
    //('options:', options);
    this.companyInfo = uni.getStorageSync('companyInfo')
    //('companyInfo:', this.companyInfo);
    this.productId = options.id;
    console.log('this.productId:', this.productId);

    // 从缓存中读取入口标识（在所有模式下都读取）
    const storedEntry = uni.getStorageSync('businessEntry');
    this.businessEntry = storedEntry || '';
    //('从缓存读取入口标识:', {
    //   stored: storedEntry,
    //   businessEntry: this.businessEntry,
    //   type: typeof storedEntry,
    //   allStorage: uni.getStorageInfoSync()
    // });

    // 接收productType参数（从URL参数或缓存中获取）
    if (options.productType) {
      this.productType = options.productType;
      uni.setStorageSync('productType', options.productType);
      console.log('从URL接收productType:', this.productType);
    } else {
      // 从缓存中读取productType
      const storedProductType = uni.getStorageSync('productType');
      if (storedProductType) {
        this.productType = storedProductType;
        console.log('从缓存读取productType:', this.productType);
      }
    }

    // 检查是否是申请调用模式
    if (options.isApplyCall === 'true') {
      this.isApplyCall = true;
      this.sourcePeriodId = options.sourcePeriodId || '';
      this.sourceMerchantId = options.sourceMerchantId || '';

      // 🔑 从URL参数获取源商家类型
      let receivedSourceMerchantType = parseInt(options.sourceMerchantType) || 0;

      // 保存源商家类型
      if (receivedSourceMerchantType > 0) {
        this.sourceMerchantType = receivedSourceMerchantType;
      } else {
        // 如果没有传递，使用推测值
        this.sourceMerchantType = Math.max(1, this.companyInfo.businessType - 1);
        console.warn('⚠️ sourceMerchantType 未传递，使用推测值:', this.sourceMerchantType);
      }

      this.sourceSkuId = options.skuId || ''; // 保存源SKU ID
      this.applyCallPrice = parseFloat(options.price) || 0;
      this.isEditMode = true; // 申请调用也是编辑模式，需要回显数据

      console.log('✅ 申请调用模式参数:', {
        currentMerchantType: this.companyInfo.businessType,
        receivedSourceMerchantType: receivedSourceMerchantType,
        finalSourceMerchantType: this.sourceMerchantType,
        sourcePeriodId: this.sourcePeriodId,
        sourceMerchantId: this.sourceMerchantId,
        sourceSkuId: this.sourceSkuId,
        applyCallPrice: this.applyCallPrice,
        productType: this.productType,
        entry: this.businessEntry
      });
    } else if (options.mode != null && options.mode !== '') {
      this.isEditMode = true;
    }

    // 如果不是申请调用模式，也记录入口标识
    if (!this.isApplyCall) {
      //('新建/编辑模式，入口标识:', this.businessEntry);
    }

    //('this.productId:', this.productId);
    //('this.isEditMode:', this.isEditMode);


    this.productStore = useProductStore();
    console.log('this.productStore.subPageData初始化', this.productStore.subPageData)
    if (!this.productStore.subPageData.groupRulesData) {
      this.productStore.subPageData.groupRulesData = JSON.parse(JSON.stringify(this.groupRulesData));
    } else {
      this.applyGroupRulesFromSource(this.productStore.subPageData.groupRulesData);
    }
    // 初始化保障提示（固定内容）
    const guaranteePromptText = '为切实履行好普通消费者权益保护等方面的义务，纤程旅行会基于安全和公平交易原则对包括但不限于扰乱系统、网络攻击、刷单、倒卖、转售订单及包括但不限于注册账号、用户信息、交易数量存在异常的订单进行风险交易判定，此部分不符合交易安全和公平原则的订单纤程旅行有权采取警示、暂停或者终止服务等措施且不承担因此原因取消订单的任何责任。';
    if (!this.productStore.subPageData.guaranteePrompt) {
      this.productStore.subPageData.guaranteePrompt = [{
        title: '保障提示',
        alt: guaranteePromptText
      }];
    }

    // 初始化风险提示（固定内容）
    // const riskPromptText = '旅游过程中可能遇到自然灾害、交通延误、健康问题等不可预测因素，导致行程调整或延误。请游客自行购买旅行保险，确保人身和财产安全。旅游活动中需遵守当地法律法规，并服从导游安排。特殊健康状况的游客应提前告知并谨慎选择项目。请妥善保管个人物品，避免丢失或被盗。若因游客个人原因造成的任何损失或事故，旅游公司不承担责任。旅行前请详细了解行程安排与注意事项。';
    // if (!this.productStore.subPageData.riskPrompt) {
    //   this.productStore.subPageData.riskPrompt = [{
    //     title: '风险提示',
    //     alt: riskPromptText
    //   }];
    // }

    // 如果是编辑模式，从API加载数据
    if (this.isEditMode && this.productId != null) {
      // 获取产品详情并加载到store
      this.loadProductDetail(this.productId);
    } else {
      // 新建模式：清空 store 中残留的数据
      //('新建模式：重置表单数据');
      this.productStore.resetForm();
    }

    // 初始化监听表单变化，设置自动保存
    this.initFormWatcher();
  },
  onShow() {
    // 强制刷新页面数据（防止数据更新但视图不更新的问题）
    this.$forceUpdate();

    // 重新加载数据以确保显示最新内容
    this.loadDataFromStore();
  },
  // 监听页面返回
  onBackPress() {
    // 检查是否正在保存
    if (this.isSaving) {
      uni.showToast({ title: '正在保存，请稍候...', icon: 'none' });
      return true; // 正在保存时阻止返回
    }

    // 检查是否有内容（至少有图片或标题）
    const hasContent = (this.form.images && this.form.images.length > 0) ||
      (this.form.name && this.form.name.trim() !== '');

    if (!hasContent) {
      // 没有任何内容，直接返回
      return false; // 允许返回
    }

    // 显示保存草稿提示
    uni.showModal({
      title: '提示',
      content: '是否需要保存当前内容为草稿？',
      confirmText: '保存草稿',
      cancelText: '直接退出',
      success: async (res) => {
        if (res.confirm) {
          // 用户选择保存草稿，传递 true 表示保存后自动返回
          await this.saveDraft(true);
        } else if (res.cancel) {
          // 用户选择直接退出
          // uni.navigateBack();
        }
      }
    });

    return true; // 阻止默认返回行为，等待用户选择
  },
  // 监听页面隐藏（小程序最小化或切换到后台）  后台挂起先不提示，只提示回退
  // onHide() {
  //   // 小程序切换前后台时询问是否保存草稿
  //   this.handlePageLeave('切换');
  // },

  methods: {
    // 清空日历组件缓存（当二类商家创建目的地接待或综合研学业务时）
    clearCalendarCacheIfNeeded() {
      try {
        const companyInfo = uni.getStorageSync("companyInfo");
        if (!companyInfo) {
          return;
        }
        
        const businessType = companyInfo.businessType;
        const productTypeNum = Number(this.productType);
        
        // 判断是否是二类商家（businessType === 2）且是目的地接待（productType === 3）或综合研学业务（productType === 2）
        if (businessType === 2 && (productTypeNum === 2 || productTypeNum === 3)) {
          console.log('清空日历组件缓存 - 二类商家创建目的地接待或综合研学业务');
          
          // 清空日历组件相关的所有缓存
          uni.removeStorageSync('existingSelectedDates');
          uni.removeStorageSync('periodSelectParams');
          uni.removeStorageSync('periodList');
          uni.removeStorageSync('outerPeriodList');
          uni.removeStorageSync('allowedSelectDates');
          uni.removeStorageSync('selectedDatesA');
          uni.removeStorageSync('selectedDatesB');
          uni.removeStorageSync('selectedDatesBForPeriodList');
          uni.removeStorageSync('selectedDates');
          
          console.log('日历组件缓存已清空');
        }
      } catch (error) {
        console.error('清空日历组件缓存失败:', error);
      }
    },
    
    // 获取费用不含内容（兼容字符串和数组）
    getExpensesExcludedContent() {
      const data = this.productStore.subPageData?.expensesExcluded;
      if (!data) return '';
      ('getExpensesExcludedContent费用不含', data);

      let content = '';
      if (typeof data === 'string') {
        content = data.trim();
      } else if (Array.isArray(data) && data.length > 0) {
        content = data
          .map(item => (item && (item.alt || item.content)) ? (item.alt || item.content) : '')
          .join('\n')
          .trim();
      }

      //('getExpensesExcludedContent处理后的内容:', content, '长度:', content.length);
      return content;
    },

    // 获取另付费项目内容（兼容字符串和数组）
    getOtherExpensesContent() {
      const data = this.productStore.subPageData?.otherExpenses;
      if (!data) return '';

      let content = '';
      if (typeof data === 'string') {
        content = data.trim();
      } else if (Array.isArray(data) && data.length > 0) {
        content = data
          .map(item => (item && (item.alt || item.content)) ? (item.alt || item.content) : '')
          .join('\n')
          .trim();
      }

      //('getOtherExpensesContent处理后的内容:', content, '长度:', content.length);
      return content;
    },

    // 获取风险提示内容（兼容字符串和数组）
    getRiskPromptContent() {
      const data = this.productStore.subPageData?.riskPrompt;
      if (!data) return '';

      let content = '';
      if (typeof data === 'string') {
        content = data.trim();
      } else if (Array.isArray(data) && data.length > 0) {
        content = data.map(item => item.alt || '').join('\n').trim();
      }

      return content;
    },

    // 获取自定义预定须知列表
    getCustomNoticesList() {
      console.log('this.productStore.subPageData自定义须知', this.productStore?.subPageData);
      let data = this.productStore?.subPageData?.customNotices ?? this.productStore?.subPageData?.customizationPrompt ?? [];
      // 统一转为数组并做安全处理
      if (Array.isArray(data)) {
        return data
          .map(item => ({
            title: (item && item.title) || '',
            alt: (item && (item.alt || item.content)) || ''
          }))
          .filter(i => i.title || i.alt);
      }
      if (typeof data === 'string') {
        const text = data.trim();
        return text ? [{ title: '', alt: text }] : [];
      }
      return [];
    },

    // 是否有自定义预定须知
    hasCustomNotices() {
      const data = this.productStore.subPageData?.customNotices;
      //('hasCustomNotices原始数据（从productStore）:', data);
      return data && Array.isArray(data) && data.length > 0;
    },
    // 获取描述标签
    getDescriptionLabel(type) {
      const labels = {
        '集散': '集散说明',
        '交通': '交通说明',
        '餐饮': '用餐说明',
        '活动': '活动描述',
        '住宿': '住宿说明',
        '用餐': '用餐说明'
      };
      return labels[type] || '详细描述';
    },

    // 获取地址名称（拆分后的第二部分，显示在上面）
    getAddressName(address) {
      if (!address) return '';
      if (address.includes('-')) {
        const parts = address.split('-');
        return parts[1] ? parts[1].trim() : '';
      }
      return '';
    },

    // 获取地址位置（拆分后的第一部分，显示在下面）
    getAddressLocation(address) {
      if (!address) return '';
      if (address.includes('-')) {
        const parts = address.split('-');
        return parts[0].trim();
      }
      return address; // 如果没有"-"，返回原地址
    },

    // 初始化表单监听器
    initFormWatcher() {
      // 使用setInterval模拟深度监听
      this.formWatcherInterval = setInterval(() => {
        if (this.productStore && typeof this.productStore.scheduleAutoSave === 'function') {
          this.productStore.scheduleAutoSave();
        }
      }, 2000); // 每2秒检查一次
    },
    beforeUnmount() {
      // 清除表单监听器定时器
      if (this.formWatcherInterval) {
        clearInterval(this.formWatcherInterval);
        this.formWatcherInterval = null;
      }
    },

    // 跳转到自定义须知页面
    goToCustomNotice() {
      uni.navigateTo({
        url: '/public/create/rules?type=customNotice'
      });
    },

    // 导航到协议页面
    goAgreement() {
      this.saveCurrentState();
      // 所有数据都通过productStore管理，无需额外保存到本地存储
      // uni.navigateTo({ 
      //   url: '/pages/merchant/create/rules' 
      // });
    },

    // 显示协议弹窗
    showAgreement(agreement) {
      // 将协议名称映射到弹窗中使用的标识
      let agreementKey = ''
      if (agreement === '境内研学接团协议' || agreement === '接团协议') {
        agreementKey = '接团协议'
      } else if (agreement === '境内研学参营协议' || agreement === '参营协议') {
        agreementKey = '参营协议'
      } else if (agreement === '境内研学随队协议' || agreement === '随队协议') {
        agreementKey = '随队协议'
      } else {
        // 如果都不匹配，尝试从协议名称中提取关键词
        if (agreement && agreement.includes('接团')) {
          agreementKey = '接团协议'
        } else if (agreement && agreement.includes('参营')) {
          agreementKey = '参营协议'
        } else if (agreement && agreement.includes('随队')) {
          agreementKey = '随队协议'
        } else {
          agreementKey = agreement || '接团协议' // 默认显示接团协议
        }
      }
      this.currentAgreement = agreementKey;
      this.popupTitle = agreement || agreementKey;
      this.showPopup = true;
    },

    // 关闭协议弹窗
    closeAgreementPopup() {
      this.showPopup = false;
    },

    // 弹窗关闭回调
    onPopupClose() {
      this.showPopup = false;
    },

    // 处理已阅读协议单选框变化
    handleReadChange(value) {
      this.hasReadAgreement = value;
    },
    //编辑模式加载详情
    async loadProductDetail(productId) {

      try {

        let merchantType;

        if (this.isApplyCall) {
          // 申请调用模式：使用源商家类型（从URL参数获取）
          merchantType = this.sourceMerchantType || Math.max(1, this.companyInfo.businessType - 1);
          console.log('🔵 申请调用模式 - merchantType:', merchantType, 'sourceMerchantType:', this.sourceMerchantType);
        } else {
          // 编辑/创建模式：使用当前商家类型
          merchantType = this.companyInfo.businessType;
          console.log('🔵 编辑/创建模式 - merchantType:', merchantType);
        }



        //获取产品信息
        const res = await request({
          url: `/product-spu/getProduct?id=${productId}&merchantType=${merchantType}`,
          method: 'GET'
        });
        console.log('📦 产品详情响应:', res);
        console.log('productId', productId)
        console.log('merchantType', merchantType)

        // 🔑 关键修改：根据 productType 决定应该使用哪个数据源（与 mergeAllProducts 逻辑一致）
        // productType=1（泛营地接待-B1创）和 productType=3（目的地接待-B2创）使用 p1 数据
        // productType=2（综合研学业务-B2创）和 productType=4（组团业务-B2调B1）使用 p2 数据
        // productType=5（研学活动-B3调B2）使用 p3 数据
        // 未指定productType时，使用旧逻辑兼容（优先级：p3 > p2 > p1）
        
        let productType;
        let productData;
        let mainSource;

        // 获取产品的实际 productType（如果传入的是undefined，则从数据中读取）
        const actualProductType = this.productType 
          ? Number(this.productType) 
          : (res.data.p3?.productSpu?.productType || res.data.p2?.productSpu?.productType || res.data.p1?.productSpu?.productType);

        console.log('🔍 loadProductDetail - actualProductType:', actualProductType, 'p1:', !!res.data.p1, 'p2:', !!res.data.p2, 'p3:', !!res.data.p3);

        if (actualProductType == 1 || actualProductType == 3) {
          // productType=1（泛营地接待-B1创）和 productType=3（目的地接待-B2创）使用 p1 数据
          mainSource = "p1";
          productData = res.data.p1;
          productType = res.data.p1?.productSpu?.productType;
          console.log('✅ 使用 p1 数据, productType:', productType);
        } else if (actualProductType == 2 || actualProductType == 4) {
          // productType=2（综合研学业务-B2创）和 productType=4（组团业务-B2调B1）使用 p2 数据
          mainSource = "p2";
          productData = res.data.p2;
          productType = res.data.p2?.productSpu?.productType;
          console.log('✅ 使用 p2 数据, productType:', productType);
        } else if (actualProductType == 5) {
          // productType=5（研学活动-B3调B2）使用 p3 数据
          mainSource = "p3";
          productData = res.data.p3;
          productType = res.data.p3?.productSpu?.productType;
          console.log('✅ 使用 p3 数据, productType:', productType);
        } else {
          // 未指定productType时，使用旧逻辑兼容（优先级：p3 > p2 > p1）
          console.warn('⚠️ 未指定productType，使用默认逻辑');
          if (res.data.p3 != null) {
            mainSource = "p3";
            productData = res.data.p3;
            productType = res.data.p3?.productSpu?.productType;
            console.log('✅ 兜底逻辑 - 使用 p3 数据, productType:', productType);
          } else if (res.data.p2 != null) {
            mainSource = "p2";
            productData = res.data.p2;
            productType = res.data.p2?.productSpu?.productType;
            console.log('✅ 兜底逻辑 - 使用 p2 数据, productType:', productType);
          } else if (res.data.p1 != null) {
            mainSource = "p1";
            productData = res.data.p1;
            productType = res.data.p1?.productSpu?.productType;
            console.log('✅ 兜底逻辑 - 使用 p1 数据, productType:', productType);
          }
        }

        // 🔑 安全检查：如果根据 productType 选择的数据源是 null，使用兜底逻辑
        if (productData == null) {
          console.warn('⚠️ 根据productType选择的数据源为null，使用兜底逻辑', {
            productType: actualProductType,
            mainSource,
            p1: !!res.data.p1,
            p2: !!res.data.p2,
            p3: !!res.data.p3
          });

          if (res.data.p3 != null) {
            mainSource = "p3";
            productData = res.data.p3;
            productType = res.data.p3?.productSpu?.productType;
          } else if (res.data.p2 != null) {
            mainSource = "p2";
            productData = res.data.p2;
            productType = res.data.p2?.productSpu?.productType;
          } else if (res.data.p1 != null) {
            mainSource = "p1";
            productData = res.data.p1;
            productType = res.data.p1?.productSpu?.productType;
          }
        }

        console.log('🔍 最终选择的 productType:', productType, 'mainSource:', mainSource, '当前商家类型:', this.companyInfo.businessType, '源商家类型:', this.sourceMerchantType)

        // 更新 this.productType，确保使用正确的 productType
        if (productType) {
          this.productType = String(productType);
          console.log('✅ 更新 this.productType:', this.productType);
        }

        // 安全检查：如果 productData 为 null，提供错误提示
        if (!productData || !productData.productSku) {
          console.error('❌ 无法获取产品数据！', {
            productType,
            p1: !!res.data.p1,
            p2: !!res.data.p2,
            p3: !!res.data.p3,
            productData
          });
          uni.showToast({
            title: '产品数据加载失败',
            icon: 'none'
          });
          return;
        }

        this.form.images = [...productData.productSku.images]
        this.ageRange = [3, 18]
        if (productData.productSku.minYearOld) this.ageRange[0] = productData.productSku.minYearOld
        if (productData.productSku.maxYearOld) this.ageRange[1] = productData.productSku.maxYearOld
        // 申请调用模式下，如果数据已有年龄限制，则限制选择范围
        if (this.isApplyCall && productData.productSku.minYearOld && productData.productSku.maxYearOld) {
          this.ageMinLimit = productData.productSku.minYearOld;
          this.ageMaxLimit = productData.productSku.maxYearOld;
        }
        console.log('productData.productSku.customizationPrompt', productData.productSku.customizationPrompt)
        if (productData.productSku.customizationPrompt) {
          this.productStore.subPageData.customizationPrompt = JSON.parse(productData.productSku.customizationPrompt)
          this.productStore.subPageData.customNotices = this.productStore.subPageData.customizationPrompt
          // 标记数据就绪并强制刷新自定义须知渲染
          this.dataReady = true;
          // this.noticesRenderKey++;
        }
        console.log('productData.productSku', productData.productSku)
        // 申请调用模式下，记录初始图片数量，并标记这些图片不可删除
        if (this.isApplyCall) {
          this.initialImageCount = this.form.images.length;
          // 为初始图片添加标记，标识为已有的图片，不可删除
          this.form.images.forEach(img => {
            img.isOriginal = true;
          });
        }

        // 确保title是字符串类型
        this.form.title = typeof productData.productSku.title === 'string'
          ? productData.productSku.title
          : (productData.productSku.title?.text || productData.productSku.title?.name || '')
        this.initializeCategorySelectionFromTitle();
        this.form.name = productData.productSpu.name
        this.form.minYearOld = productData.productSku.minYearOld;
        this.form.maxYearOld = productData.productSku.maxYearOld;
        this.form.parentsAccompanied = productData.productSku.parentsAccompanied;
        // 确保数组存在再执行push操作
        // customizationPrompt 改为字符串
        if (productData.productSku.customizationPrompt) {
          this.productStore.subPageData.customizationPrompt =
            typeof productData.productSku.customizationPrompt === 'string'
              ? productData.productSku.customizationPrompt
              : '';
        }

        if (Array.isArray(productData.productSku.productFeatures)) {
          this.productStore.subPageData.productFeatures = [...productData.productSku.productFeatures];
        }

        if (productData.productSku.productFeaturesHtml) {
          const normalizedHtml = this.normalizeFeatureHtml(productData.productSku.productFeaturesHtml);
          this.productStore.subPageData.productFeaturesHtml = normalizedHtml;

          if (!this.productStore.subPageData.productFeatures || !this.productStore.subPageData.productFeatures.length) {
            this.productStore.subPageData.productFeatures = this.extractFeaturesFromHtml(normalizedHtml);
          }
        }

        if (Array.isArray(productData.productSku.specificItinerary)) {
          this.productStore.subPageData.specificItinerary = [...productData.productSku.specificItinerary];
        }

        if (Array.isArray(productData.productSku.expensesIncluded)) {
          this.productStore.subPageData.expensesIncluded = [...productData.productSku.expensesIncluded];
        }

        // expensesExcluded 改为字符串
        if (productData.productSku.expensesExcluded) {
          this.productStore.subPageData.expensesExcluded =
            typeof productData.productSku.expensesExcluded === 'string'
              ? productData.productSku.expensesExcluded
              : '';
        }

        // otherExpenses 改为字符串
        if (productData.productSku.otherExpenses) {
          this.productStore.subPageData.otherExpenses =
            typeof productData.productSku.otherExpenses === 'string'
              ? productData.productSku.otherExpenses
              : '';
        }

        // 处理风险提示和保障提示数据
        if (Array.isArray(productData.productSku.riskPrompt)) {
          this.productStore.subPageData.riskPrompt = [...productData.productSku.riskPrompt];
        } else if (productData.productSku.riskPrompt) {
          // 兼容旧的字符串格式，转换为数组格式
          this.productStore.subPageData.riskPrompt = [{
            title: '风险提示',
            alt: productData.productSku.riskPrompt,
            id: 1
          }];
        } else {
          this.productStore.subPageData.riskPrompt = '旅游过程中可能遇到自然灾害、交通延误、健康问题等不可预测因素，导致行程调整或延误。请游客自行购买旅行保险，确保人身和财产安全。旅游活动中需遵守当地法律法规，并服从导游安排。特殊健康状况的游客应提前告知并谨慎选择项目。请妥善保管个人物品，避免丢失或被盗。若因游客个人原因造成的任何损失或事故，旅游公司不承担责任。旅行前请详细了解行程安排与注意事项';
        }

        if (Array.isArray(productData.productSku.guaranteePrompt)) {
          this.productStore.subPageData.guaranteePrompt = [...productData.productSku.guaranteePrompt];
        } else if (productData.productSku.guaranteePrompt) {
          // 兼容旧的字符串格式，转换为数组格式
          this.productStore.subPageData.guaranteePrompt = [{
            title: '保障提示',
            alt: productData.productSku.guaranteePrompt,
            id: 1
          }];
        } else {
          this.productStore.subPageData.guaranteePrompt = [];
        }

        const groupRulesPayload =
          productData.productSku.groupRulesData ||
          productData.productSku.groupRules ||
          this.productStore.subPageData.groupRulesData;
        if (groupRulesPayload) {
          this.applyGroupRulesFromSource(groupRulesPayload);
        }

        if (!this.productStore.subPageData.groupRulesData) {
          this.productStore.subPageData.groupRulesData = JSON.parse(JSON.stringify(this.groupRulesData));
        }

      } catch (error) {
        console.error('加载产品详情失败:', error);
        uni.showToast({ title: '加载产品详情失败', icon: 'none' });
      }
    },

    // 检查是否可以删除图片
    canDeleteImage(img) {
      // 申请调用模式下，标记为原始图片的不可删除
      if (this.isApplyCall && img.isOriginal) {
        return false;
      }

      // 如果图片没有上传者信息，可以删除
      if (!img.uploadedBy) return true;
      // 只有当图片是当前用户上传的才能删除
      return img.uploadedBy === this.companyInfo.businessType;
    },

    // 获取图片上传者类型文本
    getImageUploaderText(uploadedBy) {
      const typeMap = {
        1: '地接社/场馆',
        2: '旅行社',
        3: '教培'
      };
      return typeMap[uploadedBy] || '未知';
    },

    // 保存当前页面状态到store
    saveCurrentState() {
      try {
        // 保存主表单数据到store
        this.productStore.updateMainForm({
          title: this.form.title,
          category: this.form.title,
          images: this.form.images,
          tags: this.form.tags || [],
          ageRange: [this.form.minYearOld, this.form.maxYearOld],
          needParent: this.form.parentsAccompanied,
          businessType: this.form.businessType || 0,
          channelType: this.form.channelType || '',
          campAttribute: this.form.campAttribute || ''
        });

        // 保存子页面数据到store
        // 费用数据已经通过各编辑页面直接保存到store，这里无需重复保存
      } catch (e) {
        console.error('保存当前页面状态失败:', e);
      }
    },

    goBack() {
      this.productId = null;
      this.isEditMode = false;
      uni.navigateBack();
    },
    // 参营年龄限制
    onAgeRangeChange(options) {
      console.log('options', options)
      this.form.minYearOld = options[0]
      this.form.maxYearOld = options[1]
      console.log('this.form', this.form)
    },

    async chooseImage() {
      if (this.isUploading) return;

      // 计算可上传数量，申请调用模式下最多 initialImageCount + 4
      let maxUploadCount;
      if (this.isApplyCall) {
        maxUploadCount = this.initialImageCount + 4;
      } else {
        maxUploadCount = this.maxImages;
      }

      const remain = maxUploadCount - this.form.images.length;
      if (remain <= 0) return;

      this.isUploading = true;
      // 使用非阻塞的toast提示
      uni.showToast({
        title: '正在选择图片...',
        icon: 'loading',
        duration: 1000
      });

      try {
        const res = await new Promise((resolve, reject) => {
          uni.chooseImage({
            count: remain,
            sizeType: ['original'],
            success: resolve,
            fail: reject,
          });
        });

        console.log('图片', res);
        const tempFiles = res.tempFiles || [];

        const validFiles = [];

        for (let i = 0; i < tempFiles.length; i++) {
          const file = tempFiles[i];
          let path = file.path || (res.tempFilePaths ? res.tempFilePaths[i] : '');
          if (!path) continue;

          try {
            const imageInfo = await uni.getImageInfo({ src: path });
            console.log(`📐 图片${i + 1} 尺寸: ${imageInfo.width}x${imageInfo.height}`);
            if (imageInfo.width < 600 || imageInfo.height < 600) {
              uni.showModal({
                title: '提示',
                content: '图片分辨率需至少600×600，请重新选择。',
                showCancel: false
              });
              continue;
            }
            validFiles.push({ path, file, index: i });
          } catch (infoErr) {
            console.error('获取图片信息失败:', infoErr);
          }
        }

        if (!validFiles.length) {
          this.showProgressBar = false;
          this.progressText = '';
          this.progressPercentage = 0;
          uni.showModal({
            title: '提示',
            content: '上传的图片必须满足600×600以上的像素要求，请重新选择。',
            showCancel: false
          });
          return;
        }

        // 显示处理进度，但不阻塞操作
        this.showProcessingProgress(0, validFiles.length);

        const results = [];
        let processedCount = 0;

        for (let i = 0; i < validFiles.length; i++) {
          const { path: originalPath, file, index } = validFiles[i];
          let path = originalPath;

          // 📊 日志1：获取原始图片大小
          try {
            const originalFileInfo = await uni.getFileInfo({ filePath: path });
            const originalSizeMB = (originalFileInfo.size / 1024 / 1024).toFixed(2);
            console.log(`📷 图片${index + 1} - 原始大小: ${originalSizeMB}MB (${originalFileInfo.size}字节)`);
          } catch (e) {
            console.error('获取原始图片大小失败:', e);
          }

          // 显示裁剪选项
          const cropResult = await this.showCropOptions(path);
          if (!cropResult) {
            // 用户取消裁剪，跳过这张图片
            continue;
          }

          path = cropResult;

          // 📊 日志2：获取裁剪后图片大小
          try {
            const croppedFileInfo = await uni.getFileInfo({ filePath: path });
            const croppedSizeMB = (croppedFileInfo.size / 1024 / 1024).toFixed(2);
            console.log(`✂️ 图片${index + 1} - 裁剪后大小: ${croppedSizeMB}MB (${croppedFileInfo.size}字节)`);
          } catch (e) {
            console.error('获取裁剪后图片大小失败:', e);
          }

          // 📊 日志3：获取准备上传到服务器的图片大小（不再压缩）
          try {
            const uploadFileInfo = await uni.getFileInfo({ filePath: path });
            const uploadSizeMB = (uploadFileInfo.size / 1024 / 1024).toFixed(2);
            console.log(`📤 图片${index + 1} - 准备上传到服务器，大小: ${uploadSizeMB}MB (${uploadFileInfo.size}字节)`);
          } catch (e) {
            console.error('获取上传前图片大小失败:', e);
          }

          // 上传图片到服务器
          try {
            const uploadRes = await this.uploadImageToServer(path, index + 1);
            processedCount++;
            // 更新处理进度
            this.showProcessingProgress(processedCount, validFiles.length);

            // 创建符合数据库结构的图片对象，使用服务器返回的URL
            results.push({
              url: uploadRes.url,
              alt: "",
              order: this.form.images.length + results.length,
              width: uploadRes.width || 0,
              height: uploadRes.height || 0,
              uploadedBy: this.companyInfo.businessType, // 记录上传者类型
              isOriginal: false // 新上传的图片标记为非原始图片
            });
          } catch (uploadErr) {
            console.error('上传图片失败:', uploadErr);
            uni.showToast({
              title: '部分图片上传失败',
              icon: 'none',
              duration: 2000
            });
          }
        }

        if (results.length) {
          this.showProgressBar = false;
          this.progressText = '';
          this.progressPercentage = 0;

          // 计算最大图片数量
          let maxCount;
          if (this.isApplyCall) {
            maxCount = this.initialImageCount + 4;
          } else {
            maxCount = this.maxImages;
          }

          // 合并并保持最多maxCount张图片
          this.form.images = this.form.images.concat(results).slice(0, maxCount);
          // 重新计算order属性，确保连续不重复
          this.form.images.forEach((img, index) => {
            img.order = index;
          });

          // 📊 汇总日志
          console.log(`🎉 图片上传完成！共处理 ${results.length} 张图片`);
          console.log('================================================');

          uni.showToast({
            title: `成功上传${results.length}张图片`,
            icon: 'success'
          });
        } else {
          this.showProgressBar = false;
          this.progressText = '';
          this.progressPercentage = 0;
          uni.showToast({
            title: '未上传任何图片',
            icon: 'none'
          });
        }
      } catch (err) {
        console.error('选择图片失败:', err);
        // ignore cancel
      } finally {
        uni.hideLoading();
        this.isUploading = false;
      }
    },
    /**
     * 显示裁剪选项
     * @param {String} imagePath 图片路径
     * @returns {Promise<String|null>} 返回裁剪后的图片路径，用户取消则返回null
     */
    showCropOptions(imagePath) {
      return new Promise((resolve) => {
        // 直接进入手动裁剪，不再显示选择操作
        this.cropImage(imagePath).then(resolve).catch(() => resolve(null));
      });
    },
    /**
     * 裁剪图片
     * @param {String} imagePath 图片路径
     * @returns {Promise<String>} 返回裁剪后的图片路径
     */
    cropImage(imagePath) {
      return new Promise((resolve, reject) => {
        // 显示裁剪弹窗
        this.showCropModal(imagePath, resolve, reject);
      });
    },
    /**
     * 显示裁剪弹窗
     * @param {String} imagePath 图片路径
     * @param {Function} resolve 成功回调
     * @param {Function} reject 失败回调
     */
    showCropModal(imagePath, resolve, reject) {
      // 设置当前裁剪的图片
      this.cropImagePath = imagePath;
      this.cropResolve = resolve;
      this.cropReject = reject;
      this.showCropPopup = true;

      // 初始化裁剪
      this.initCrop();
    },

    /**
     * 初始化裁剪
     */
    initCrop() {
      if (!this.cropImagePath) return;

      // iOS 兼容性：使用 $nextTick 确保 DOM 渲染完成
      this.$nextTick(() => {
        // 延迟一下，确保弹窗完全显示
        setTimeout(() => {
          // 先获取系统信息和设置canvas尺寸
          const systemInfo = uni.getSystemInfoSync();
          const windowWidth = systemInfo.windowWidth;
          // 1:1的比例，使用屏幕宽度作为正方形边长
          this.canvasSize = {
            width: windowWidth,
            height: windowWidth
          };
          //('Canvas尺寸:', this.canvasSize);

          // 获取图片信息
          uni.getImageInfo({
            src: this.cropImagePath,
            success: (imageInfo) => {
              this.imageInfo = imageInfo;
              //('图片信息:', imageInfo);
              console.log('图片信息:', imageInfo);
              // 初始化裁剪框位置（固定）
              this.initCropFrame();

              // 初始化图片位置
              this.initImagePosition();

              // iOS 需要再延迟一下才能正确绘制
              setTimeout(() => {
                this.drawCropPreview();
              }, 100);
            },
            fail: (error) => {
              console.error('获取图片信息失败:', error);
              uni.showToast({
                title: '图片加载失败',
                icon: 'none'
              });
            }
          });
        }, 200);
      });
    },

    /**
     * 初始化裁剪框位置 - 根据图片尺寸自适应 (1:1正方形)
     */
    initCropFrame() {
      const { width: canvasWidth, height: canvasHeight } = this.canvasSize;
      const { width: imgWidth, height: imgHeight } = this.imageInfo;

      // 验证canvas尺寸
      if (!canvasWidth || !canvasHeight || isNaN(canvasWidth) || isNaN(canvasHeight) ||
        canvasWidth <= 0 || canvasHeight <= 0) {
        // 使用屏幕宽度作为默认值
        const screenWidth = uni.getSystemInfoSync().screenWidth;
        this.cropFrame = {
          x: 0,
          y: 0,
          width: screenWidth,
          height: screenWidth // 1:1正方形
        };
        //('裁剪框尺寸(默认):', this.cropFrame);
        return;
      }

      // 计算图片在canvas中的显示尺寸
      const scaleX = canvasWidth / imgWidth;
      const scaleY = canvasHeight / imgHeight;
      const fitScale = Math.min(scaleX, scaleY); // 取较小值，确保图片完整显示
      const displayWidth = imgWidth * fitScale;
      const displayHeight = imgHeight * fitScale;

      // 裁剪框大小：以图片的较小边为准，创建1:1正方形
      // 如果宽度较大，裁剪框大小与高度一样；如果高度较大，则与宽度一样
      const cropSize = Math.min(displayWidth, displayHeight);

      // 居中显示裁剪框
      const cropX = (canvasWidth - cropSize) / 2;
      const cropY = (canvasHeight - cropSize) / 2;

      this.cropFrame = {
        x: cropX,
        y: cropY,
        width: cropSize,
        height: cropSize
      };


    },
    /**
     * 初始化图片位置 - 图片缩放以适配裁剪框
     */
    initImagePosition() {
      const { width: imgWidth, height: imgHeight } = this.imageInfo;
      const { width: canvasWidth, height: canvasHeight } = this.canvasSize;
      const { x: cropX, y: cropY, width: cropWidth, height: cropHeight } = this.cropFrame;

      // 计算缩放比例，使图片能够完全填充裁剪框
      const scaleX = cropWidth / imgWidth;
      const scaleY = cropHeight / imgHeight;
      // 取较大值，确保图片能完全覆盖裁剪框
      const fitScale = Math.max(scaleX, scaleY);

      // 计算图片在canvas中的显示尺寸
      const displayWidth = imgWidth * fitScale;
      const displayHeight = imgHeight * fitScale;

      // 图片左上角对齐裁剪框左上角
      // 如果图片小于裁剪框，则居中
      let offsetX = cropX;
      let offsetY = cropY;

      if (displayWidth < cropWidth) {
        offsetX = cropX + (cropWidth - displayWidth) / 2;
      }
      if (displayHeight < cropHeight) {
        offsetY = cropY + (cropHeight - displayHeight) / 2;
      }

      this.imagePosition = {
        x: offsetX,
        y: offsetY,
        scale: fitScale
      };
    },
    /**
     * 绘制裁剪预览
     */
    drawCropPreview() {
      const ctx = uni.createCanvasContext('cropPreviewCanvas', this);

      // 计算图片在canvas中的显示尺寸
      const { width: imgWidth, height: imgHeight } = this.imageInfo;
      const { width: canvasWidth, height: canvasHeight } = this.canvasSize;


      // 使用图片位置和缩放
      const displayWidth = imgWidth * this.imagePosition.scale;
      const displayHeight = imgHeight * this.imagePosition.scale;

      // 先清空整个 canvas
      ctx.clearRect(0, 0, canvasWidth, canvasHeight);

      // 绘制完整的图片
      ctx.drawImage(
        this.cropImagePath,
        this.imagePosition.x,
        this.imagePosition.y,
        displayWidth,
        displayHeight
      );

      // 绘制半透明遮罩（裁剪框以外的区域）
      const { x: cropX, y: cropY, width: cropWidth, height: cropHeight } = this.cropFrame;
      ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';

      // 绘制四个矩形的遮罩
      // 上方
      ctx.fillRect(0, 0, canvasWidth, cropY);
      // 下方
      ctx.fillRect(0, cropY + cropHeight, canvasWidth, canvasHeight - (cropY + cropHeight));
      // 左侧
      ctx.fillRect(0, cropY, cropX, cropHeight);
      // 右侧
      ctx.fillRect(cropX + cropWidth, cropY, canvasWidth - (cropX + cropWidth), cropHeight);

      ctx.draw();
    },

    /**
     * 限制图片缩放比例
     */
    constrainImageScale() {
      const { width: imgWidth, height: imgHeight } = this.imageInfo;
      const { width: canvasWidth, height: canvasHeight } = this.canvasSize;
      const { x: cropX, y: cropY, width: cropWidth, height: cropHeight } = this.cropFrame;

      // 计算最小缩放（图片必须至少覆盖整个裁剪框）
      const minScaleX = cropWidth / imgWidth;
      const minScaleY = cropHeight / imgHeight;
      const minScale = Math.max(minScaleX, minScaleY);

      // 计算最大缩放（防止图片过大）
      const maxScale = Math.max(minScale * 3, 5); // 最大放大到初始大小的5倍

      // 限制缩放范围
      this.imagePosition.scale = Math.max(minScale, Math.min(maxScale, this.imagePosition.scale));
    },

    /**
     * 限制图片位置 - 允许在裁剪框内拖拽
     */
    constrainImagePosition() {
      const { width: canvasWidth, height: canvasHeight } = this.canvasSize;
      const { width: imgWidth, height: imgHeight } = this.imageInfo;

      // 验证输入值，防止NaN
      if (!this.imagePosition || typeof this.imagePosition.x !== 'number' || typeof this.imagePosition.y !== 'number' ||
        typeof this.imagePosition.scale !== 'number') {
        this.initImagePosition();
        return;
      }

      // 检查是否为NaN或无效值
      if (isNaN(this.imagePosition.x) || isNaN(this.imagePosition.y) || isNaN(this.imagePosition.scale)) {
        this.initImagePosition();
        return;
      }

      // 固定缩放，不允许改变
      const displayWidth = imgWidth * this.imagePosition.scale;
      const displayHeight = imgHeight * this.imagePosition.scale;

      // 获取裁剪框边界
      const { x: cropX, y: cropY, width: cropWidth, height: cropHeight } = this.cropFrame;
      const cropRight = cropX + cropWidth;
      const cropBottom = cropY + cropHeight;

      // 限制图片位置，使裁剪框能够完全覆盖图片内容
      // 图片的左边不能超过裁剪框的左边
      const minX = cropX - (displayWidth - cropWidth);
      // 图片的左边不能小于裁剪框的左边
      const maxX = cropX;

      // 图片的上边不能超过裁剪框的上边
      const minY = cropY - (displayHeight - cropHeight);
      // 图片的上边不能小于裁剪框的上边
      const maxY = cropY;

      // 应用边界限制
      this.imagePosition.x = Math.max(minX, Math.min(maxX, this.imagePosition.x));
      this.imagePosition.y = Math.max(minY, Math.min(maxY, this.imagePosition.y));

    },

    /**
     * 图片触摸开始
     * @param {Object} event 触摸事件
     */
    onCropTouchStart(event) {
      event.preventDefault(); // 阻止默认行为

      // 缓存触摸点
      this.touchCache = [];
      for (let i = 0; i < event.touches.length; i++) {
        const touch = event.touches[i];
        this.touchCache.push({
          x: touch.x || touch.clientX,
          y: touch.y || touch.clientY
        });
      }

      // 如果只有一个触摸点，开始拖拽
      if (this.touchCache.length === 1) {
        const touch = event.touches[0];

        if (!touch) {
          return;
        }

        // 确保图片位置数据有效
        if (!this.imagePosition || isNaN(this.imagePosition.x) || isNaN(this.imagePosition.y) ||
          isNaN(this.imagePosition.scale)) {
          this.initImagePosition();
        }

        // 开始拖拽
        this.isDragging = true;
        this.isPinching = false;

        // 兼容不同平台的坐标获取方式
        this.dragStartPos = {
          x: touch.x || touch.clientX,
          y: touch.y || touch.clientY
        };
        this.originalImagePos = {
          x: this.imagePosition.x,
          y: this.imagePosition.y,
          scale: this.imagePosition.scale
        };

        // 验证原始图片位置数据
        if (isNaN(this.originalImagePos.x) || isNaN(this.originalImagePos.y) ||
          isNaN(this.originalImagePos.scale)) {
          this.isDragging = false;
          return;
        }
      }
      // 如果有两个触摸点，开始捏合缩放
      else if (this.touchCache.length === 2) {
        this.isDragging = false;
        this.isPinching = true;

        // 计算两指间的距离
        const distance = this.getDistance(
          this.touchCache[0].x, this.touchCache[0].y,
          this.touchCache[1].x, this.touchCache[1].y
        );

        this.pinchStartDistance = distance;
        this.pinchStartScale = this.imagePosition.scale;

      }
    },

    /**
     * 计算两点间的距离
     */
    getDistance(x1, y1, x2, y2) {
      const dx = x2 - x1;
      const dy = y2 - y1;
      return Math.sqrt(dx * dx + dy * dy);
    },

    /**
     * 图片触摸移动
     * @param {Object} event 触摸事件
     */
    onCropTouchMove(event) {
      event.preventDefault(); // 阻止默认行为

      // 更新触摸点缓存
      this.touchCache = [];
      for (let i = 0; i < event.touches.length; i++) {
        const touch = event.touches[i];
        this.touchCache.push({
          x: touch.x || touch.clientX,
          y: touch.y || touch.clientY
        });
      }

      // 处理捏合缩放
      if (this.isPinching && this.touchCache.length === 2) {
        // 计算当前两指间的距离
        const distance = this.getDistance(
          this.touchCache[0].x, this.touchCache[0].y,
          this.touchCache[1].x, this.touchCache[1].y
        );

        // 计算缩放比例
        const scale = distance / this.pinchStartDistance;
        const newScale = this.pinchStartScale * scale;

        // 更新图片缩放
        this.imagePosition.scale = newScale;

        // 限制缩放范围
        this.constrainImageScale();

        // 应用位置限制
        this.constrainImagePosition();


        // 重绘预览
        this.drawCropPreview();
        return;
      }

      // 处理拖拽
      if (!this.isDragging) {
        return;
      }

      const touch = event.touches[0];

      if (!touch || !this.dragStartPos || !this.originalImagePos) {
        return;
      }

      // 兼容不同平台的坐标获取方式
      const currentX = touch.x || touch.clientX;
      const currentY = touch.y || touch.clientY;

      // 计算移动距离
      const deltaX = currentX - this.dragStartPos.x;
      const deltaY = currentY - this.dragStartPos.y;


      // 验证计算结果
      if (isNaN(deltaX) || isNaN(deltaY)) {
        return;
      }

      // 移动图片
      const newX = this.originalImagePos.x + deltaX;
      const newY = this.originalImagePos.y + deltaY;

      // 验证新位置
      if (isNaN(newX) || isNaN(newY)) {
        return;
      }

      // 更新图片位置
      this.imagePosition = {
        x: newX,
        y: newY,
        scale: this.originalImagePos.scale
      };


      // 应用边界限制
      this.constrainImagePosition();

      // 重绘预览
      this.drawCropPreview();
    },
    /**
     * 图片触摸结束
     */
    onCropTouchEnd(event) {

      // 重置状态
      this.isDragging = false;
      this.isPinching = false;
      this.touchCache = [];

      // 如果只有一个触摸点且在拖拽状态，保持拖拽状态
      if (event.touches && event.touches.length === 1) {
        // 重新开始单指拖拽
        const touch = event.touches[0];
        if (touch) {
          this.isDragging = true;
          this.dragStartPos = {
            x: touch.x || touch.clientX,
            y: touch.y || touch.clientY
          };
          this.originalImagePos = {
            x: this.imagePosition.x,
            y: this.imagePosition.y,
            scale: this.imagePosition.scale
          };
        }
      }
    },

    /**
     * 确认裁剪
     */
    confirmCrop() {
      if (!this.cropImagePath) {
        uni.showToast({ title: '请先选择图片', icon: 'none' });
        return;
      }

      // 使用非阻塞的toast提示
      uni.showToast({
        title: '正在裁剪...',
        icon: 'loading',
        duration: 2000
      });

      // 执行裁剪
      this.performCrop().then((croppedPath) => {
        this.showCropPopup = false;
        if (this.cropResolve) {
          this.cropResolve(croppedPath);
        }
      }).catch((error) => {
        uni.showToast({ title: '裁剪失败', icon: 'none' });
        if (this.cropReject) {
          this.cropReject(error);
        }
      });
    },

    /**
     * 执行裁剪
     * @returns {Promise<String>} 返回裁剪后的图片路径
     */
    performCrop() {
      return new Promise((resolve, reject) => {
        const ctx = uni.createCanvasContext('cropCanvas', this);

        // 计算图片在canvas中的显示尺寸和偏移
        const { width: imgWidth, height: imgHeight } = this.imageInfo;

        // 使用图片位置和缩放
        const displayWidth = imgWidth * this.imagePosition.scale;
        const displayHeight = imgHeight * this.imagePosition.scale;

        // 计算裁剪区域在原图中的位置
        const cropX = (this.cropFrame.x - this.imagePosition.x) / this.imagePosition.scale;
        const cropY = (this.cropFrame.y - this.imagePosition.y) / this.imagePosition.scale;
        const cropWidth = this.cropFrame.width / this.imagePosition.scale;
        const cropHeight = this.cropFrame.height / this.imagePosition.scale;

        const outputWidth = Math.max(1, Math.round(cropWidth));
        const outputHeight = Math.max(1, Math.round(cropHeight));
        console.log(`✂️ 裁剪输出尺寸(裁剪区域): ${outputWidth}x${outputHeight} 像素`);

        const targetSize = 800;
        console.log(`✂️ 裁剪导出尺寸固定为: ${targetSize}x${targetSize} 像素`);

        // 绘制裁剪后的图片，按照目标尺寸输出
        ctx.clearRect(0, 0, targetSize, targetSize);
        ctx.drawImage(
          this.cropImagePath,
          cropX, cropY, cropWidth, cropHeight,
          0, 0, targetSize, targetSize
        );

        ctx.draw(false, () => {
          // 导出裁剪后的图片
          uni.canvasToTempFilePath({
            canvasId: 'cropCanvas',
            width: targetSize,
            height: targetSize,
            destWidth: targetSize,
            destHeight: targetSize,
            fileType: 'jpg',
            quality: 0.85,
            success: (res) => {
              resolve(res.tempFilePath);
            },
            fail: reject
          }, this);
        });
      });
    },

    /**
     * 取消裁剪
     */
    cancelCrop() {
      this.showCropPopup = false;

      // 隐藏进度条
      this.showProgressBar = false;
      this.progressText = '';
      this.progressPercentage = 0;

      // 重置裁剪相关状态
      this.isDragging = false;
      this.isPinching = false;
      this.touchCache = [];

      if (this.cropReject) {
        this.cropReject(new Error('用户取消裁剪'));
      }
    },



    /**
     * 显示处理进度（非阻塞）
     * @param {Number} current 当前进度
     * @param {Number} total 总数
     */
    showProcessingProgress(current, total) {
      const percentage = Math.round((current / total) * 100);
      const message = current === total ? '处理完成' : `正在处理 ${current}/${total}`;

      // 更新进度条状态
      this.progressText = message;
      this.progressPercentage = percentage;

      if (current === 0) {
        // 开始处理时显示进度条
        this.showProgressBar = true;
      } else if (current === total) {
        // 处理完成时隐藏进度条
        setTimeout(() => {
          this.showProgressBar = false;
        }, 1500);
      }

      // 同时使用toast显示进度，不阻塞用户操作
      uni.showToast({
        title: message,
        icon: current === total ? 'success' : 'loading',
        duration: current === total ? 1500 : 1000
      });
    },
    /**
     * 上传图片到服务器
     * @param {String} filePath 本地临时文件路径
     * @param {Number} imageIndex 图片索引（用于日志）
     * @returns {Promise<Object>} 返回服务器URL等信息
     */
    uploadImageToServer(filePath, imageIndex = 0) {
      return new Promise((resolve, reject) => {

        const token = uni.getStorageSync('accessToken') || '';

        if (!token) {
          reject(new Error('未登录，请先登录'));
          return;
        }

        console.log(`⬆️ 图片${imageIndex} - 开始上传到服务器:`, filePath);
        const uploadTask = uni.uploadFile({
          url: `https://abc.qctrips.cn/local-file/upload`,
          filePath: filePath,
          name: 'file',
          timeout: 60000, // 60秒超时
          header: {
            'Authorization': token
          },
          success: (uploadRes) => {

            if (uploadRes.statusCode === 200) {
              try {
                const data = JSON.parse(uploadRes.data);

                if (data.code === 200 && data.data) {
                  const imageUrl = data.data.url || data.data.objectUrl;

                  // 📊 日志5：服务器保存成功
                  console.log(`✅ 图片${imageIndex} - 上传成功，服务器URL:`, imageUrl);
                  if (data.data.size) {
                    const serverSizeMB = (data.data.size / 1024 / 1024).toFixed(2);
                    console.log(`💾 图片${imageIndex} - 服务器存储大小: ${serverSizeMB}MB (${data.data.size}字节)`);
                  }

                  resolve({
                    url: imageUrl,
                    width: 0,
                    height: 0
                  });
                } else {
                  console.error(`❌ 图片${imageIndex} - 上传失败:`, data.msg || data.message);
                  reject(new Error(data.msg || data.message || '上传失败'));
                }
              } catch (e) {
                console.error(`❌ 图片${imageIndex} - 解析服务器响应失败:`, e);
                reject(new Error('解析服务器响应失败'));
              }
            } else {
              console.error(`❌ 图片${imageIndex} - HTTP错误:`, uploadRes.statusCode);
              reject(new Error(`上传失败，HTTP ${uploadRes.statusCode}`));
            }
          },
          fail: (err) => {

            // 判断具体错误类型
            if (err.errMsg) {
              if (err.errMsg.includes('timeout')) {
                console.error(`❌ 图片${imageIndex} - 上传超时`);
                reject(new Error('上传超时，请检查网络'));
              } else if (err.errMsg.includes('fail')) {
                console.error(`❌ 图片${imageIndex} - 网络请求失败`);
                reject(new Error('网络请求失败，请重试'));
              } else {
                console.error(`❌ 图片${imageIndex} - 上传失败:`, err.errMsg);
                reject(new Error(err.errMsg));
              }
            } else {
              console.error(`❌ 图片${imageIndex} - 上传失败`);
              reject(new Error('上传失败，请重试'));
            }
          }
        });

        // 监听上传进度
        uploadTask.onProgressUpdate((res) => {
          // 可以在这里添加上传进度日志
          // console.log(`📊 图片${imageIndex} - 上传进度: ${res.progress}%`);
        });
      });
    },

    confirmRemove(index) {
      const image = this.form.images[index];

      // 申请调用模式下，原始图片不可删除
      if (this.isApplyCall && image.isOriginal) {
        uni.showModal({
          title: "提示",
          content: "无法删除一类商家的图片，只能删除新上传的图片",
          showCancel: false,
          confirmText: "确定"
        });
        return;
      }

      // 检查图片是否是当前用户上传的
      const canDelete = !image.uploadedBy || image.uploadedBy === this.companyInfo.businessType;

      if (!canDelete) {
        uni.showModal({
          title: "权限不足",
          content: "您只能删除自己上传的图片，无法删除其他用户上传的图片",
          showCancel: false,
          confirmText: "确定"
        });
        return;
      }

      uni.showModal({
        title: "提示",
        content: "确定删除这张图片吗？",
        success: (res) => {
          if (res.confirm) {
            this.form.images.splice(index, 1);
            // 重新计算剩余图片的order属性，确保连续不重复
            this.form.images.forEach((img, idx) => {
              img.order = idx;
            });
          }
        },
      });
    },

    // 一级类别选择变化
    onMainCategoryChange(e) {
      if (this.disableCategorySelection) return;
      const index = Number(e.detail.value);

      if (index === this.categoryOptions.length) {
        this.enterCustomCategoryMode(true);
        this.syncCategoryTitle();
        return;
      }

      this.exitCustomCategoryMode();
      this.form.currentMainCategoryIndex = index;
      const subCategories = this.categoryOptions[index]?.child || [];
      this.form.currentSubCategoryIndex = subCategories.length > 0 ? 0 : -1;
      this.syncCategoryTitle();
    },

    // 二级类别选择变化
    onSubCategoryChange(e) {
      if (this.disableCategorySelection || this.isCustomCategoryMode) return;
      if (!this.resolvedMainCategory) return;
      const index = Number(e.detail.value);
      this.form.currentSubCategoryIndex = index;
      this.syncCategoryTitle();
    },

    // 标题输入事件
    onTitleInput(e) {
      // 输入时不做任何处理，让用户自由输入
    },

    // 处理产品特色点击
    handleFeatureClick() {
      if (this.shouldDisableFeature) {
        return; // 二类商家-组团业务 或 三类商家禁止编辑
      }
      this.saveCurrentState();
      uni.navigateTo({
        url: '/public/create/featureEditor'
      });
    },
    // 处理产品特色自定义点击
    handleFeatureCustomClick() {
      this.saveCurrentState();
      uni.navigateTo({
        url: '/public/create/featureEditor?isCustom=true'
      });
    },
    normalizeFeatureHtml(html) {
      if (!html) {
        return html;
      }

      return html.replace(/<img[^>]*>/gi, (imgTag) => {
        let cleanedTag = imgTag;

        cleanedTag = cleanedTag.replace(/\s+(width|height)\s*=\s*"[^"]*"/gi, '');
        cleanedTag = cleanedTag.replace(/\s+(width|height)\s*=\s*'[^']*'/gi, '');

        cleanedTag = cleanedTag.replace(/\s+style\s*=\s*"([^"]*)"/gi, (match, styleValue) => {
          const normalizedStyles = styleValue
            .split(';')
            .map(style => style.trim())
            .filter(style => style && !/^width\s*:/i.test(style) && !/^height\s*:/i.test(style));

          normalizedStyles.push('width:100% !important');
          normalizedStyles.push('max-width:100% !important');
          normalizedStyles.push('height:auto !important');
          normalizedStyles.push('display:block');

          const dedupedStyles = Array.from(new Set(normalizedStyles));

          return ` style="${dedupedStyles.join('; ')}"`;
        });

        if (!/\sstyle=/i.test(cleanedTag)) {
          cleanedTag = cleanedTag.replace(
            /<img/,
            '<img style="width:100% !important; max-width:100% !important; height:auto !important; display:block"'
          );
        }

        return cleanedTag;
      });
    },

    extractFeaturesFromHtml(html) {
      const features = [];
      let order = 0;

      if (!html || !html.trim()) {
        return features;
      }

      const pRegex = /<p[^>]*>(.*?)<\/p>/gi;
      const matches = [];

      let pMatch;
      while ((pMatch = pRegex.exec(html)) !== null) {
        const content = pMatch[1];
        const imgMatch = content.match(/<img[^>]+src=["']([^"']+)["'][^>]*>/i);

        if (imgMatch) {
          const imgIndex = content.indexOf(imgMatch[0]);

          const textBefore = content.substring(0, imgIndex)
            .replace(/<[^>]*>/g, '')
            .replace(/&nbsp;/g, ' ')
            .trim();

          const textAfter = content.substring(imgIndex + imgMatch[0].length)
            .replace(/<[^>]*>/g, '')
            .replace(/&nbsp;/g, ' ')
            .trim();

          if (textBefore) {
            matches.push({
              index: pMatch.index,
              type: 'text',
              alt: textBefore,
              order: order++
            });
          }

          matches.push({
            index: pMatch.index + imgIndex,
            type: 'image',
            imgUrl: imgMatch[1],
            order: order++
          });

          if (textAfter) {
            matches.push({
              index: pMatch.index + imgIndex + imgMatch[0].length,
              type: 'text',
              alt: textAfter,
              order: order++
            });
          }
        } else {
          const textContent = content
            .replace(/<[^>]*>/g, '')
            .replace(/&nbsp;/g, ' ')
            .trim();

          if (textContent) {
            matches.push({
              index: pMatch.index,
              type: 'text',
              alt: textContent,
              order: order++
            });
          }
        }
      }

      matches.sort((a, b) => {
        if (a.index !== b.index) {
          return a.index - b.index;
        }
        return a.order - b.order;
      });

      matches.forEach((match, index) => {
        match.order = index;
      });

      matches.forEach(match => {
        if (match.type === 'image') {
          features.push({ type: 'image', imgUrl: match.imgUrl, alt: '产品特色图片', order: match.order });
        } else if (match.type === 'text') {
          features.push({ type: 'text', alt: match.alt, imgUrl: '', order: match.order });
        }
      });

      return features;
    },

    // 跳转到产品特色编辑页面
    goToFeatureEditor() {
      this.saveCurrentState();
      uni.navigateTo({
        url: '/public/create/featureEditor'
      });
    },

    // 图片加载失败处理
    onImageError(event, index) {
    },

    // 图片加载成功处理
    onImageLoad(url) {
    },

    // 处理具体行程点击
    handleItineraryClick() {
      if (this.isBusinessType3) {
        return; // 三类商家禁止编辑
      }
      this.goToItinerary();
    },

    // 跳转到行程页面
    goToItinerary() {
      this.saveCurrentState();
      const url = this.isApplyCall 
        ? `/public/create/itinerary?isApplyCall=true`
        : '/public/create/itinerary';
      uni.navigateTo({
        url: url
      })
    },
    // 处理费用点击
    handleExpenseClick(type) {
      if (this.isBusinessType3) {
        return; // 三类商家禁止编辑
      }

      // 费用不含和另付费项目跳转到 rules 页面
      if (type === 'excluded' || type === 'other') {
        const fieldName = type === 'excluded' ? 'expensesExcluded' : 'otherExpenses';
        const fieldData = this.productStore.subPageData && this.productStore.subPageData[fieldName];
        let isNew = true;

        if (fieldData) {
          if (typeof fieldData === 'string') {
            isNew = fieldData.trim() === '';
          } else if (Array.isArray(fieldData)) {
            isNew = fieldData.length === 0;
          }
        }

        uni.navigateTo({
          url: `/public/create/rules?type=${type}&isNew=${isNew}`
        });
        return;
      }

      this.goToExpenses(type);
    },

    // 处理风险提示点击
    handleRiskPromptClick() {
      if (this.isBusinessType3) {
        return; // 三类商家禁止编辑
      }

      const riskPromptData = this.productStore.subPageData && this.productStore.subPageData.riskPrompt;
      let isNew = true;

      if (riskPromptData) {
        if (typeof riskPromptData === 'string') {
          isNew = riskPromptData.trim() === '';
        } else if (Array.isArray(riskPromptData)) {
          isNew = riskPromptData.length === 0;
        }
      }

      uni.navigateTo({
        url: `/public/create/rules?type=riskPrompt&isNew=${isNew}`
      });
    },

    // 处理自定义提示点击
    handleCustomizationPromptClick() {
      if (this.isBusinessType3) {
        return; // 三类商家禁止编辑
      }

      const customizationPromptData = this.productStore.subPageData && this.productStore.subPageData.customizationPrompt;
      let isNew = true;

      if (customizationPromptData) {
        if (typeof customizationPromptData === 'string') {
          isNew = customizationPromptData.trim() === '';
        } else if (Array.isArray(customizationPromptData)) {
          isNew = customizationPromptData.length === 0;
        }
      }

      uni.navigateTo({
        url: `/public/create/rules?type=customizationPrompt&isNew=${isNew}`
      });
    },

    // 处理自定义预定须知点击
    handleCustomNoticeClick(notice = null, index = -1) {
      if (this.isBusinessType3) {
        return; // 三类商家禁止编辑
      }

      // 如果有传入 notice 和 index，说明是编辑现有须知
      if (notice && index >= 0) {
        // 将当前须知数据存储到临时位置，供 rules.vue 使用
        uni.setStorageSync('editingCustomNotice', {
          notice: notice,
          index: index
        });

        uni.navigateTo({
          url: `/public/create/rules?type=custom&isNew=false&isEdit=true&index=${index}`
        });
      } else {
        // 新增自定义须知
        uni.navigateTo({
          url: `/public/create/rules?type=custom&isNew=true`
        });
      }
    },
    // 删除自定义预定须知（清空 customizationPrompt）
    deleteCustomNotice(index) {
      if (this.isBusinessType3) {
        return; // 三类商家禁止删除
      }

      uni.showModal({
        title: '提示',
        content: '确定要删除这个自定义须知吗？',
        success: (res) => {
          if (res.confirm) {
            // customizationPrompt：判断空/字符串/数组 → 统一转数组 → 删除 → 回写为字符串
            let cpArray = [];
            const cpRaw = this.productStore.subPageData.customizationPrompt;
            if (Array.isArray(cpRaw)) {
              cpArray = cpRaw.slice();
            } else if (typeof cpRaw === 'string' && cpRaw.trim()) {
              try {
                const parsed = JSON.parse(cpRaw);
                cpArray = Array.isArray(parsed) ? parsed : [];
              } catch (e) {
                cpArray = [];
              }
            }
            if (index >= 0 && index < cpArray.length) {
              cpArray.splice(index, 1);
            }
            this.productStore.subPageData.customizationPrompt = cpArray.length ? JSON.stringify(cpArray) : '';

            // customNotices：同样的处理
            let noticesArray = [];
            const noticesRaw = this.productStore.subPageData.customNotices;
            if (Array.isArray(noticesRaw)) {
              noticesArray = noticesRaw.slice();
            } else if (typeof noticesRaw === 'string' && noticesRaw.trim()) {
              try {
                const parsedNotices = JSON.parse(noticesRaw);
                noticesArray = Array.isArray(parsedNotices) ? parsedNotices : [];
              } catch (e) {
                noticesArray = [];
              }
            }
            if (index >= 0 && index < noticesArray.length) {
              noticesArray.splice(index, 1);
            }
            this.productStore.subPageData.customNotices = noticesArray.length ? JSON.stringify(noticesArray) : '';

            // 强制更新视图
            this.$forceUpdate();

            uni.showToast({
              title: '删除成功',
              icon: 'success'
            });
          }
        }
      });
    },
    // 跳转到费用页面
    goToExpenses(type) {
      this.saveCurrentState();
      uni.navigateTo({
        url: `/public/create/expenses?type=${type}`
      });
    },

    // 展开/收起卡片
    toggleSection(key) {
      return;
      this.expanded[key] = !this.expanded[key];
    },

    // 处理成团条款点击
    handleGroupRulesClick() {
      if (this.isBusinessType3) {
        return; // 三类商家禁止编辑
      }
      this.showGroupRulesModal();
    },

    // 显示成团条款弹窗
    showGroupRulesModal() {
      this.showGroupRulesModalFlag = true;
    },

    // 隐藏成团条款弹窗
    hideGroupRulesModal() {
      this.showGroupRulesModalFlag = false;
    },

    // 选择成团条款模式
    selectGroupRulesMode(mode) {
      this.groupRulesData.mode = mode;
      const selectedOption = this.groupRulesModeOptions.find(option => option.value === mode);
      this.groupRulesData.modeText = selectedOption ? selectedOption.text : '';

      // 根据模式设置默认参数
      this.setDefaultGroupRulesParams(mode);
    },

    // 设置默认参数
    setDefaultGroupRulesParams(mode) {
      switch (mode) {
        case 'province_1_2':
          this.groupRulesData.leastNumberOfPeople = 24;
          this.groupRulesData.fullNumberOfPeople = 45;
          this.setConfirmDaysRange(7, 15);
          this.groupRulesData.policies = [
            { days: '15日及以上', percentage: 5 }
          ];
          break;
        case 'province_3_plus':
          this.groupRulesData.leastNumberOfPeople = 20;
          this.groupRulesData.fullNumberOfPeople = 40;
          this.setConfirmDaysRange(10, 20);
          this.groupRulesData.policies = [
            { days: '20日及以上', percentage: 5 }
          ];
          break;
        case 'cross_province_3_plus':
          this.groupRulesData.leastNumberOfPeople = 15;
          this.groupRulesData.fullNumberOfPeople = 35;
          this.setConfirmDaysRange(15, 30);
          this.groupRulesData.policies = [
            { days: '30日及以上', percentage: 5 }
          ];
          break;
      }
      this.enforceGroupRulesBounds('minPeople');
      this.enforceGroupRulesBounds('maxPeople');
    },

    // 设置行程前确认天数范围，同时同步字符串表示
    setConfirmDaysRange(start, end) {
      const normalizedStart = this.normalizeConfirmDayValue(start);
      const normalizedEnd = this.normalizeConfirmDayValue(end);
      this.$set(this.groupRulesData, 'applicationDeadline', normalizedStart === '' ? '' : normalizedStart);
      this.$set(this.groupRulesData, 'confirmationTime', normalizedEnd === '' ? '' : normalizedEnd);
      this.enforceGroupRulesBounds('applicationDeadline');
      this.enforceGroupRulesBounds('confirmationTime');
      this.syncConfirmDaysString();
    },

    handleGroupRulesInput(field, event) {
      const raw = event && event.detail ? event.detail.value : event;
      if (raw === '' || raw === null || raw === undefined) {
        this.$set(this.groupRulesData, field, '');
        this.setGroupRulesClampFlag(field, false);
        return;
      }
      let numeric = this.normalizeIntegerValue(raw);
      if (numeric === '') {
        this.$set(this.groupRulesData, field, '');
        this.setGroupRulesClampFlag(field, false);
        return;
      }

      let exceeded = false;
      const limits = GROUP_RULES_FIELD_LIMITS[field];
      if (limits) {
        if (numeric < limits.min) {
          numeric = limits.min;
          exceeded = true;
        } else if (numeric > limits.max) {
          numeric = limits.max;
          exceeded = true;
        }
      }

      this.$set(this.groupRulesData, field, numeric);
      const relationChanged = this.enforceGroupRulesCrossRules(field);
      this.setGroupRulesClampFlag(field, exceeded || relationChanged);

      if (field === 'applicationDeadline' || field === 'confirmationTime') {
        this.syncConfirmDaysString();
      }
    },

    handleGroupRulesChange(field, event) {
      this.handleGroupRulesInput(field, event);
      this.showGroupRulesLimitIfNeeded(field);
      this.enforceGroupRulesCrossRules(field, true);
      if (field === 'applicationDeadline' || field === 'confirmationTime') {
        this.syncConfirmDaysString();
      }
      this.setGroupRulesClampFlag(field, false);
    },

    handleGroupRulesBlur(field) {
      this.showGroupRulesLimitIfNeeded(field);
      this.enforceGroupRulesCrossRules(field, true);
      if (field === 'applicationDeadline' || field === 'confirmationTime') {
        this.syncConfirmDaysString();
      }
      this.setGroupRulesClampFlag(field, false);
    },

    enforceGroupRulesBounds(field, showTip = false) {
      const limits = GROUP_RULES_FIELD_LIMITS[field];
      if (!limits) {
        return false;
      }

      const currentValue = this.groupRulesData[field];
      if (currentValue === '' || currentValue === null || currentValue === undefined) {
        return false;
      }

      let parsed = this.normalizeIntegerValue(currentValue);
      let tipNeeded = false;

      if (parsed === '') {
        parsed = limits.min;
        tipNeeded = true;
      }

      const clamped = Math.min(limits.max, Math.max(limits.min, parsed));
      if (clamped !== parsed) {
        tipNeeded = true;
      }

      if (this.groupRulesData[field] !== clamped) {
        this.$set(this.groupRulesData, field, clamped);
      }

      if (showTip && tipNeeded) {
        this.showGroupRulesLimitToast(field, limits.min, limits.max);
      }

      return tipNeeded;
    },

    enforceGroupRulesCrossRules(field, showTip = false) {
      let changed = false;
      const {
        leastNumberOfPeople,
        fullNumberOfPeople,
        applicationDeadline,
        confirmationTime
      } = this.groupRulesData;

      if (leastNumberOfPeople !== '' && fullNumberOfPeople !== '' && leastNumberOfPeople > fullNumberOfPeople) {
        if (field === 'leastNumberOfPeople') {
          if (this.groupRulesData.fullNumberOfPeople !== leastNumberOfPeople) {
            this.groupRulesData.fullNumberOfPeople = leastNumberOfPeople;
            changed = true;
            if (showTip) {
              this.showRelationToast('最多满员人数需不小于最低成团人数');
            }
          }
        } else if (field === 'fullNumberOfPeople') {
          if (this.groupRulesData.leastNumberOfPeople !== fullNumberOfPeople) {
            this.groupRulesData.leastNumberOfPeople = fullNumberOfPeople;
            changed = true;
            if (showTip) {
              this.showRelationToast('最低成团人数需不大于最多满员人数');
            }
          }
        } else {
          if (this.groupRulesData.leastNumberOfPeople > this.groupRulesData.fullNumberOfPeople) {
            this.groupRulesData.fullNumberOfPeople = this.groupRulesData.leastNumberOfPeople;
            changed = true;
          }
        }
      }

      if (applicationDeadline !== '' && confirmationTime !== '' && confirmationTime < applicationDeadline) {
        if (field === 'applicationDeadline') {
          if (this.groupRulesData.confirmationTime !== applicationDeadline) {
            this.groupRulesData.confirmationTime = applicationDeadline;
            changed = true;
            if (showTip) {
              this.showRelationToast('成团确认天数需不小于申请截止天数');
            }
          }
        } else if (field === 'confirmationTime') {
          if (this.groupRulesData.applicationDeadline !== confirmationTime) {
            this.groupRulesData.applicationDeadline = confirmationTime;
            changed = true;
            if (showTip) {
              this.showRelationToast('申请截止天数需不大于成团确认天数');
            }
          }
        } else {
          if (this.groupRulesData.confirmationTime < this.groupRulesData.applicationDeadline) {
            this.groupRulesData.confirmationTime = this.groupRulesData.applicationDeadline;
            changed = true;
          }
        }
      }

      return changed;
    },

    showGroupRulesLimitToast(field, min, max) {
      const label = GROUP_RULES_FIELD_LABELS[field] || '该字段';
      const unit = GROUP_RULES_FIELD_UNITS[field] || '';
      uni.showToast({
        title: `${label}需在${min}-${max}${unit}之间`,
        icon: 'none',
        duration: 2000
      });
    },

    showGroupRulesLimitIfNeeded(field) {
      if (!field) return;
      if (this.groupRulesClampFlags[field]) {
        const limits = GROUP_RULES_FIELD_LIMITS[field];
        if (limits) {
          this.showGroupRulesLimitToast(field, limits.min, limits.max);
        }
        this.$set(this.groupRulesClampFlags, field, false);
      }
    },

    setGroupRulesClampFlag(field, changed) {
      if (!field) return;
      if (changed) {
        this.$set(this.groupRulesClampFlags, field, true);
      } else if (this.groupRulesClampFlags[field]) {
        this.$set(this.groupRulesClampFlags, field, false);
      }
    },

    showRelationToast(message) {
      uni.showToast({
        title: message,
        icon: 'none',
        duration: 2000
      });
    },

    // 将外部数据应用到本地成团条款数据，兼容不同字段格式
    applyGroupRulesFromSource(raw) {
      const normalized = this.normalizeGroupRulesPayload(raw);
      if (!normalized) return;

      // 合并字段
      Object.assign(this.groupRulesData, normalized);

      // 若外部提供了confirmDays字符串但未提供上下限，尝试解析
      if ((normalized.confirmDays && normalized.confirmDays.trim()) &&
        (normalized.applicationDeadline === undefined && normalized.confirmationTime === undefined)) {
        const parsedRange = this.parseConfirmDaysRangeString(normalized.confirmDays);
        if (parsedRange.start !== undefined && parsedRange.start !== null) {
          this.groupRulesData.applicationDeadline = parsedRange.start === '' ? '' : parsedRange.start;
        }
        if (parsedRange.end !== undefined && parsedRange.end !== null) {
          this.groupRulesData.confirmationTime = parsedRange.end === '' ? '' : parsedRange.end;
        }
      }

      ['leastNumberOfPeople', 'fullNumberOfPeople', 'applicationDeadline', 'confirmationTime'].forEach(field => {
        this.enforceGroupRulesBounds(field);
      });

      ['leastNumberOfPeople', 'fullNumberOfPeople', 'applicationDeadline', 'confirmationTime'].forEach(field => {
        this.enforceGroupRulesCrossRules(field);
      });

      this.syncConfirmDaysString();
    },

    // 兼容API/旧字段的成团条款数据格式
    normalizeGroupRulesPayload(raw) {
      if (!raw || typeof raw !== 'object') return null;

      const result = {};
      if (raw.mode !== undefined && raw.mode !== null) {
        result.mode = raw.mode;
      }
      if (raw.modeText !== undefined && raw.modeText !== null) {
        result.modeText = raw.modeText;
      } else if (raw.mode_text !== undefined && raw.mode_text !== null) {
        result.modeText = raw.mode_text;
      }

      const leastNumberOfPeople = this.normalizeIntegerValue(
        raw.leastNumberOfPeople ?? raw.least_number_of_people ?? raw.minPeople ?? raw.min_people ?? raw.minGroupSize ?? raw.min_group_size
      );
      if (leastNumberOfPeople !== '') {
        result.leastNumberOfPeople = leastNumberOfPeople;
      }

      const fullNumberOfPeople = this.normalizeIntegerValue(
        raw.fullNumberOfPeople ?? raw.full_number_of_people ?? raw.maxPeople ?? raw.max_people ?? raw.maxGroupSize ?? raw.max_group_size
      );
      if (fullNumberOfPeople !== '') {
        result.fullNumberOfPeople = fullNumberOfPeople;
      }

      const deadline = this.normalizeConfirmDayValue(
        raw.applicationDeadline ?? raw.application_deadline ?? raw.confirmDaysStart ?? raw.confirm_days_start
      );
      if (deadline !== '') {
        result.applicationDeadline = deadline;
      }

      const confirmation = this.normalizeConfirmDayValue(
        raw.confirmationTime ?? raw.confirmation_time ?? raw.confirmDaysEnd ?? raw.confirm_days_end
      );
      if (confirmation !== '') {
        result.confirmationTime = confirmation;
      }

      if (typeof raw.confirmDays === 'string' && raw.confirmDays.trim()) {
        result.confirmDays = raw.confirmDays.trim();
      } else if (Array.isArray(raw.confirm_days) && raw.confirm_days.length) {
        result.confirmDays = raw.confirm_days.join('-');
      }

      if (Array.isArray(raw.policies)) {
        result.policies = raw.policies.map(item => ({
          days: item?.days ?? item?.range ?? '',
          percentage: this.toNumberOrEmpty(item?.percentage ?? item?.rate ?? item?.value ?? '')
        }));
      } else if (raw.policyList && Array.isArray(raw.policyList)) {
        result.policies = raw.policyList.map(item => ({
          days: item?.days ?? '',
          percentage: this.toNumberOrEmpty(item?.percentage ?? '')
        }));
      }

      return result;
    },

    // 归一化天数输入，返回数字或空字符串
    normalizeConfirmDayValue(value) {
      if (value === null || value === undefined || value === '') {
        return '';
      }
      const num = Number(value);
      if (Number.isFinite(num)) {
        return Number.isInteger(num) ? num : Number(num.toFixed(2));
      }
      return '';
    },

    // 根据上下限同步字符串形式，兼容旧数据结构
    syncConfirmDaysString() {
      const start = this.normalizeConfirmDayValue(this.groupRulesData.applicationDeadline);
      const end = this.normalizeConfirmDayValue(this.groupRulesData.confirmationTime);
      const hasStart = start !== '';
      const hasEnd = end !== '';
      if (!hasStart && !hasEnd) {
        this.groupRulesData.confirmDays = '';
        return;
      }
      if (hasStart && hasEnd) {
        this.groupRulesData.confirmDays = `${start}-${end}`;
        return;
      }
      this.groupRulesData.confirmDays = hasStart ? `${start}` : `${end}`;
    },

    // 归一化成团人数输入（返回整数或空字符串）
    normalizeIntegerValue(value) {
      if (value === null || value === undefined || value === '') {
        return '';
      }
      const num = Number(value);
      if (!Number.isFinite(num)) {
        return '';
      }
      return Math.max(0, Math.round(num));
    },

    // 将值转换为数字或空字符串
    toNumberOrEmpty(value) {
      if (value === null || value === undefined || value === '') {
        return '';
      }
      const num = Number(value);
      if (Number.isFinite(num)) {
        return num;
      }
      return '';
    },

    // 解析类似"3-7" "3~7" "3至7"格式的字符串
    parseConfirmDaysRangeString(str) {
      if (typeof str !== 'string') return {};
      const matches = str.match(/\d+(\.\d+)?/g);
      if (!matches || !matches.length) {
        return {};
      }
      if (matches.length >= 2) {
        return {
          start: this.normalizeConfirmDayValue(matches[0]),
          end: this.normalizeConfirmDayValue(matches[1])
        };
      }
      return {
        start: this.normalizeConfirmDayValue(matches[0])
      };
    },

    // 添加退团政策
    addPolicy() {
      this.groupRulesData.policies.push({
        days: '',
        percentage: 0
      });
    },

    // 删除退团政策
    deletePolicy(index) {
      if (this.groupRulesData.policies.length > 1) {
        this.groupRulesData.policies.splice(index, 1);
      }
    },

    // 保存成团条款设置
    saveGroupRules() {
      // 这里可以添加保存逻辑，比如保存到store或发送到服务器
      //('保存成团条款设置:', this.groupRulesData);
      uni.showToast({
        title: '设置已保存',
        icon: 'success'
      });
      this.hideGroupRulesModal();
    },

    // 存草稿
    // autoNavigateBack: 是否在保存成功后自动返回（默认true）
    async saveDraft(autoNavigateBack = true) {
      // 申请调用模式下不允许存草稿
      if (this.isApplyCall) {
        uni.showToast({ title: '申请调用模式不可存草稿', icon: 'none' });
        return false;
      }
      // 最基本的验证：至少需要有图片和标题
      if (!this.form.images || this.form.images.length === 0) {
        uni.showToast({ title: '请至少上传一张图片', icon: 'none' });
        return false;
      }

      if (!this.form.name || this.form.name.trim() === '') {
        uni.showToast({ title: '请输入产品标题', icon: 'none' });
        return false;
      }

      if (this.isSaving) {
        return false;
      }

      this.isSaving = true;

      try {
        const companyInfo = uni.getStorageSync("companyInfo");
        if (!companyInfo) {
          uni.showToast({ title: '请先完善商家信息', icon: 'none' });
          this.isSaving = false;
          return false;
        }

        // 准备草稿数据
        const draftData = this.prepareSubmitData();
        console.log('📝 准备保存草稿的数据:', JSON.stringify(draftData, null, 2));
        console.log('📝 草稿 customizationPrompt:', draftData.productSku.customizationPrompt);
        // 将 status 设置为 0（草稿箱）
        draftData.productSpu.status = 0;


        // 如果是编辑模式，使用更新接口
        if (this.isEditMode && this.productId) {
          const res = await request({
            url: `/product-spu/update?spuId=${this.productId}`,
            method: 'PUT',
            data: draftData
          });

          if (res.code === 200) {
            uni.showToast({
              title: '草稿已保存',
              icon: 'success',
              duration: 1500
            });

            if (autoNavigateBack) {
              setTimeout(() => {
                // uni.navigateBack(); //不需要自动返回
              }, 1500);
            }
            return true;
          } else {
            throw new Error(res.message || '保存草稿失败');
          }
        } else {
          // 新建模式，调用保存草稿接口
          const res = await request({
            url: '/product-spu/saveDraft',
            method: 'POST',
            data: draftData
          });

          if (res.code === 200) {
            // 保存成功后，将产品ID保存，切换到编辑模式
            this.productId = res.data;
            this.isEditMode = true;

            uni.showToast({
              title: '草稿已保存',
              icon: 'success',
              duration: 1500
            });

            // if (autoNavigateBack) {
            //   // 延迟后返回上一页
            //   setTimeout(() => {
            //     uni.navigateBack();
            //   }, 1500);
            // }
            return true;
          } else {
            throw new Error(res.message || '保存草稿失败');
          }
        }
      } catch (error) {
        uni.showToast({
          title: error.message || '保存草稿失败，请重试',
          icon: 'none'
        });
        return false;
      } finally {
        this.isSaving = false;
      }
    },

    onPreview() {
      if (!this.isFormValid) {
        uni.showToast({ title: '请填写必要信息', icon: 'none' });
        return;
      }

      // 收集当前页面的所有数据
      const previewData = {
        // 基本信息
        productName: this.form.name || '预览产品',
        price: this.form.price || 0.00,
        minYearOld: this.form.minYearOld || 6,
        maxYearOld: this.form.maxYearOld || 18,
        parentsAccompanied: this.form.parentsAccompanied || 0,

        // 图片数据
        images: this.form.images || [],

        // 班期数据
        dateList: this.form.dateList || [],

        // 产品特色
        productFeatures: this.productStore.subPageData?.productFeatures || '',

        // 具体行程
        specificItinerary: this.productStore.subPageData?.specificItinerary || [],

        // 费用信息
        expensesIncluded: this.productStore.subPageData?.expensesIncluded || [],
        expensesExcluded: this.productStore.subPageData?.expensesExcluded || [],
        otherExpenses: this.productStore.subPageData?.otherExpenses || [],

        // 预定须知
        bookingInfo: this.productStore.subPageData?.bookingInfo || '请提前3天预订，预订成功后不可取消。如需改期，请提前24小时联系客服。',

        // 保障提示
        guaranteePrompt: this.productStore.subPageData?.guaranteePrompt || '本产品提供全程保险保障，确保游客安全。',

        // 风险提示
        riskPrompt: this.productStore.subPageData?.riskPrompt || '户外活动存在一定风险，请游客注意安全，遵守导游安排。',

        // 成团/退团/违约条款
        groupFormation: this.groupRulesData?.groupFormation || '成团说明：本次旅游团人数为24人起，如未达到成团人数，将提前3天通知并全额退款。',
        refundRules: this.groupRulesData?.refundRules || '退团规则：出发前7天内申请退团，扣除30%费用；出发前3天内申请退团，扣除50%费用。',
        breachClause: this.groupRulesData?.breachClause || '违约条款：若游客未按约定时间参加旅行或提前终止行程，将根据实际损失收取相应违约金。',
        customizationPrompt: this.productStore.subPageData.customizationPrompt
      };


      // 将预览数据存储到全局存储中
      uni.setStorageSync('previewData', previewData);

      // 跳转到预览页面
      uni.navigateTo({
        url: '/public/productInfo/productInfo?mode=preview'
      });
    },
    //保存编辑产品
    async onUpdate() {
      // if (!this.isFormValid || !this.hasReadAgreement || this.isSaving || this.isUploading)
      //   return;
      this.isSaving = true;
      try {
        const companyInfo = uni.getStorageSync("companyInfo")
        if (!companyInfo) {
          uni.showToast({ title: '请先完善商家信息', icon: 'none' });
          return;
        }
        const spuId = this.productId;
        //准备数据
        const data = this.prepareSubmitData();
        const res = await request({
          url: `/product-spu/update?spuId=${spuId}`,
          method: 'PUT',
          data: data
        });
        if (res.code == 200) {
          uni.showToast({
            title: '产品保存成功', icon: 'success'
          })
          setTimeout(() => {
            //重置表单
            this.form = {}
            this.productStore.resetForm()
            uni.navigateBack();
          }, 500);
        }
      } catch (error) {
        uni.showToast({ title: '保存失败，请重试', icon: 'none' });
      } finally {
        this.isSaving = false;
        this.productId = null;
        this.isEditMode = false;
      }
    },

    // 下一步提交创建产品
    async onNext() {
      // if (!this.isFormValid || !this.hasReadAgreement || this.isSaving || this.isUploading)
      //   return;

      // this.isSaving = true;
      const companyInfo = uni.getStorageSync("companyInfo")
      if (!companyInfo) {
        uni.showToast({ title: '请先完善商家信息', icon: 'none' });
        this.isSaving = false;
        return;
      }

      // 如果是申请调用模式，不创建产品，直接跳转到班期创建页面
      if (this.isApplyCall) {

        // 准备编辑后的产品数据，保存到 localStorage 供 periodCreate 使用
        const editedProductData = this.prepareSubmitData();
        uni.setStorageSync('applyCallEditedData', JSON.stringify(editedProductData));

        // 二类商家创建目的地接待或综合研学业务时，清空日历组件缓存
        this.clearCalendarCacheIfNeeded();

        uni.navigateTo({
          url: `/public/period/periodCreate?id=${this.productId}&isApplyCall=true&sourcePeriodId=${this.sourcePeriodId}&sourceMerchantId=${this.sourceMerchantId}&sourceMerchantType=${this.sourceMerchantType}&sourceSkuId=${this.sourceSkuId}${this.productType ? `&productType=${this.productType}` : ''}`
        });
        this.isSaving = false;
        return;
      }

      //准备数据
      const data = this.prepareSubmitData();
      console.log('📦 准备提交的完整数据:', JSON.stringify(data, null, 2));
      console.log('📦 productSku.customizationPrompt:', data.productSku.customizationPrompt);
      // 如果是编辑模式（从首页点击编辑进来），调用更新接口
      if (this.isEditMode && this.productId) {
        try {
          const res = await request({
            url: `/product-spu/update?spuId=${this.productId}`,
            method: 'PUT',
            data: data
          });

          if (res.code === 200) {
            uni.showToast({ title: '操作成功', icon: 'success' });
            //延迟0.5s再跳转 到创建班期页(上架设置页)
            setTimeout(() => {
              // 二类商家创建目的地接待或综合研学业务时，清空日历组件缓存
              this.clearCalendarCacheIfNeeded();
              const productTypeParam = this.productType ? `&productType=${this.productType}` : '';
              uni.navigateTo({ url: `/public/period/periodCreate?id=${this.productId}${productTypeParam}` });
            }, 500);
          } else {
            uni.showToast({ title: res.message || '操作失败', icon: 'none' });
            this.isSaving = false;
          }
        } catch (error) {
          uni.showToast({ title: '操作失败，请重试', icon: 'none' });
          this.isSaving = false;
        }
        return;
      }

      // 新建模式，调用创建接口
      try {
        const res = await request({
          url: '/product-spu/create',
          method: 'POST',
          data: data
        });

        if (res.code === 200) {
          uni.showToast({ title: '产品创建成功', icon: 'success' });
          // 保存产品ID
          this.productId = res.data;
          //延迟0.5s再跳转 到创建班期页(上架设置页)
          setTimeout(() => {
            // 二类商家创建目的地接待或综合研学业务时，清空日历组件缓存
            this.clearCalendarCacheIfNeeded();
            const productTypeParam = this.productType ? `&productType=${this.productType}` : '';
            uni.navigateTo({ url: `/public/period/periodCreate?id=${this.productId}${productTypeParam}` });
          }, 500);
        } else {
          uni.showToast({ title: res.message || '创建失败', icon: 'none' });
          this.isSaving = false;
        }
      } catch (error) {
        uni.showToast({ title: '创建失败，请重试', icon: 'none' });
        this.isSaving = false;
      }
    },
    // 准备提交/编辑数据
    prepareSubmitData() {
      const companyInfo = uni.getStorageSync("companyInfo")
      console.log('提交的时候this.form', this.form, this.getCustomNoticesList())
      const subPageData = this.productStore && this.productStore.subPageData
        ? this.productStore.subPageData
        : {};

      const normalizeOrder = (value, fallback) => {
        const parsed = Number(value);
        return Number.isFinite(parsed) ? parsed : fallback;
      };

      const sanitizeText = (value) => {
        if (value === null || value === undefined) return '';
        if (typeof value === 'string') return value.trim();
        return String(value).trim();
      };

      const mapArrayWithTitle = (list = []) => {
        if (!Array.isArray(list)) return [];
        return list
          .map((item, index) => {
            const title = sanitizeText(item?.title || '');
            const content = sanitizeText(item?.content || item?.alt || '');
            const supplement = sanitizeText(item?.supplement || '');
            const alt = Object.prototype.hasOwnProperty.call(item || {}, 'alt')
              ? sanitizeText(item.alt)
              : content;
            const order = normalizeOrder(item?.order, index + 1);
            return {
              title,
              content,
              alt,
              supplement,
              order
            };
          })
          .filter(mapped => mapped.title || mapped.content || mapped.supplement);
      };

      const mapArrayWithAltOnly = (list = []) => {
        if (!Array.isArray(list)) return [];
        return list
          .map((item, index) => {
            const title = sanitizeText(item?.title || '');
            const content = sanitizeText(item?.content || item?.alt || '');
            const supplement = sanitizeText(item?.supplement || '');
            const alt = Object.prototype.hasOwnProperty.call(item || {}, 'alt')
              ? sanitizeText(item.alt)
              : content;
            const order = normalizeOrder(item?.order, index + 1);
            return {
              title,
              content,
              alt,
              supplement,
              order
            };
          })
          .filter(mapped => mapped.title || mapped.content || mapped.supplement);
      };

      const ensureArrayField = (raw, mapperWithTitle = false) => {
        if (Array.isArray(raw)) {
          return mapperWithTitle ? mapArrayWithTitle(raw) : mapArrayWithAltOnly(raw);
        }
        if (typeof raw === 'string' && raw.trim()) {
          const content = raw.trim();
          const baseItem = {
            title: '',
            content,
            alt: content,
            supplement: '',
            order: 1
          };
          return [baseItem];
        }
        return [];
      };

      const stringifyRichText = (raw) => {
        if (Array.isArray(raw)) {
          return raw
            .map(item => (item && item.alt ? String(item.alt).trim() : ''))
            .filter(Boolean)
            .join('\n');
        }
        if (typeof raw === 'string') {
          return raw;
        }
        return '';
      };

      // 准备图片数据
      const images = this.form.images.map((img, index) => ({
        url: img.url,
        alt: img.alt || '',
        order: img.order || index + 1,
        width: img.width || 0,
        height: img.height || 0
      }));

      // 准备费用包含数据
      const expensesIncludeds = mapArrayWithTitle(subPageData.expensesIncluded);

      // 费用不含改为字符串（兼容字符串和数组格式）
      let expensesExcluded = ''
      if (typeof subPageData.expensesExcluded === 'string') {
        expensesExcluded = subPageData.expensesExcluded
      }
      if (typeof subPageData.expensesExcluded === 'array') {
        expensesExcluded = subPageData.expensesExcluded[0].alt
      }
      if (typeof subPageData.expensesExcluded === 'object') {
        expensesExcluded = subPageData.expensesExcluded[0].alt
      }

      // 其他费用改为字符串（兼容字符串和数组格式）
      let otherExpenses = ''
      if (typeof subPageData.otherExpenses === 'string') {
        otherExpenses = subPageData.otherExpenses
      }
      if (typeof subPageData.otherExpenses === 'array') {
        otherExpenses = subPageData.otherExpenses[0].alt
      }
      if (typeof subPageData.otherExpenses === 'object') {
        otherExpenses = subPageData.otherExpenses[0].alt
      }
      const riskPrompt = stringifyRichText(subPageData.riskPrompt);
      // 准备产品特色数据 - 使用新的productFeatures数组
      let productFeatures = [];
      if (Array.isArray(subPageData.productFeatures) && subPageData.productFeatures.length > 0) {
        productFeatures = [...subPageData.productFeatures];
      } else {
        // 如果没有产品特色数据，使用模拟数据作为默认值
        productFeatures = this.productFeatures;
      }
      // 准备产品特色自定义数据 - addProductFeatures
      let addProductFeatures = [];
      if (Array.isArray(subPageData.productFeaturesCustom) && subPageData.productFeaturesCustom.length > 0) {
        addProductFeatures = [...subPageData.productFeaturesCustom];
      }
      // 准备行程数据
      const specificItinerarys = [];
      if (Array.isArray(subPageData.specificItinerary) && subPageData.specificItinerary.length > 0) {
        subPageData.specificItinerary.forEach((day, index) => {
          // 处理行程项目中的时间字段，确保格式正确，并保留所有其他字段（包括description）
          const items = (day.items || []).map(item => {
            const processedItem = {
              title: item.title || '',
              alt: item.alt || '',
              address: item.address || '',
              startTime: item.startTime || '',
              endTime: item.endTime || '',
              description: item.description || '',
              order: item.order || null
            };

            // 如果有时间字段且格式不对，清空该字段
            if (processedItem.startTime && !processedItem.startTime.includes(':')) {
              processedItem.startTime = '';
            }
            if (processedItem.endTime && !processedItem.endTime.includes(':')) {
              processedItem.endTime = '';
            }

            return processedItem;
          });

          const dayData = {
            day: String(day.day || index + 1),
            main: day.main || '',
            items: items
          };

          specificItinerarys.push(dayData);

        });
      }


      const groupRules = {
        mode: sanitizeText(this.groupRulesData?.mode || ''),
        modeText: sanitizeText(this.groupRulesData?.modeText || ''),
        leastNumberOfPeople: this.normalizeIntegerValue(this.groupRulesData?.leastNumberOfPeople) || 0,
        fullNumberOfPeople: this.normalizeIntegerValue(this.groupRulesData?.fullNumberOfPeople) || 0,
        applicationDeadline: this.normalizeIntegerValue(this.groupRulesData?.applicationDeadline) || 0,
        confirmationTime: this.normalizeIntegerValue(this.groupRulesData?.confirmationTime) || 0,
        confirmDays: sanitizeText(this.groupRulesData?.confirmDays || ''),
        policies: Array.isArray(this.groupRulesData?.policies)
          ? this.groupRulesData.policies.map((item, index) => ({
            days: sanitizeText(item?.days || ''),
            percentage: this.normalizeIntegerValue(item?.percentage) || 0,
            order: index + 1
          }))
          : []
      };

      // 保障提示数据
      const guaranteePrompt = '为切实履行好普通消费者权益保护等方面的义务，纤程旅行会基于安全和公平交易原则对包括但不限于扰乱系统、网络攻击、刷单、倒卖、转售订单及包括但不限于注册账号、用户信息、交易数量存在异常的订单进行风险交易判定，此部分不符合交易安全和公平原则的订单纤程旅行有权采取警示、暂停或者终止服务等措施且不承担因此原因取消订单的任何责任。';
      // 自定义须知数据
      const customizationPrompt = this.getCustomNoticesList()
      // 商品来源，进行自用或者调用判断 1=自建 2=调用
      let originType = 0;

      // 根据用户类型和入口判断 originType
      if (companyInfo.businessType === 2) {
        // 二类用户根据入口判断
        if (this.businessEntry === 'dest_receive' || this.businessEntry === 'group_travel') {
          originType = 2; // 从目的地接待业务与组团业务进入
        } else if (this.businessEntry === 'comprehensive_research') {
          originType = 1; // 从综合研学进入
        } else {
          originType = 1; // 默认值
        }
      } else if (companyInfo.businessType === 1) {
        // 一类用户始终为1
        originType = 1;
      } else {
        // 其他情况默认为1
        originType = 1;
      }
      return {
        "businessType": companyInfo.businessType,
        "productSpu": {
          "merchantId": companyInfo.id,
          "name": this.form.name,
          "mainImage": this.form.images[0].url || '',
          "productType": this.productType,
          "originalSpuId": 0,
          "status": 1,// 1表示待审核
          "isAllowCall": 1,
          "originType": originType,
        },
        "productSku": {
          "merchantId": companyInfo.id,
          "title": this.form.title,
          "images": images,
          "productFeatures": productFeatures,
          "productFeaturesHtml": this.productStore && this.productStore.subPageData ? this.productStore.subPageData.productFeaturesHtml || '' : '',
          "addProductFeatures": addProductFeatures,
          "expensesIncluded": expensesIncludeds,
          "otherExpenses": otherExpenses,
          "expensesExcluded": expensesExcluded,
          "minYearOld": this.form.minYearOld,
          "maxYearOld": this.form.maxYearOld,
          "parentsAccompanied": this.form.parentsAccompanied ? 1 : 0,
          "riskPrompt": riskPrompt,
          "guaranteePrompt": guaranteePrompt,
          "specificItinerary": specificItinerarys,
          "customizationPrompt": JSON.stringify(customizationPrompt),
          "groupRulesData": groupRules,
          "leastNumberOfPeople": groupRules.leastNumberOfPeople,
          "fullNumberOfPeople": groupRules.fullNumberOfPeople,
          "applicationDeadline": groupRules.applicationDeadline,
          "confirmationTime": groupRules.confirmationTime,
          "confirmDays": groupRules.confirmDays,
          "groupPolicies": groupRules.policies
        }
      };

    },

    // ========== 产品特色相关方法 ==========

    // 安全获取产品特色数组
    getProductFeatures() {
      if (!this.productStore || !this.productStore.subPageData || !this.productStore.subPageData.productFeatures) {
        return [];
      }
      return this.productStore.subPageData.productFeatures;
    },

    // 处理家长陪同参营开关变化
    handleParentSwitchChange(e) {
      // 阻止事件传播和默认行为
      e.stopPropagation();

      // 确保状态始终为false
      this.$nextTick(() => {
        this.form.parentsAccompanied = false;
      });

      // 显示提示对话框
      uni.showModal({
        title: '提示',
        content: '【暂不支持"亲子营"产品经营】',
        showCancel: false
      });
    },

    // 页面加载时初始化
    onLoad() {
      // 确保家长陪同参营默认关闭
      if (!this.form) this.form = {};
      this.form.parentsAccompanied = false;

      // 初始化产品特色、具体行程、费用包含、费用不含、另付费项目的默认值
      this.initializeDefaultValues();
    },

    // 从 store 加载数据
    loadDataFromStore() {
      if (!this.productStore) {
        this.productStore = useProductStore();
      }

      if (!this.productStore.subPageData) {
        this.productStore.subPageData = {};
      }
      console.log('this.productStore.subPageData', this.productStore.subPageData.customNotices)
      if (this.productStore.subPageData.customNotices) {
        this.productStore.subPageData.customizationPrompt = this.productStore.subPageData.customNotices
        // 标记数据就绪并强制刷新自定义须知渲染
        this.dataReady = true;
        // this.noticesRenderKey++;
      }

      // 强制更新视图以显示最新的 store 数据
      this.$nextTick(() => {
        this.$forceUpdate();
      });
    },
    // 初始化默认值
    initializeDefaultValues() {
      console.log('this.productStore', this.productStore)
      if (!this.productStore) {
        this.productStore = useProductStore();
      }

      // 确保 subPageData 存在
      if (!this.productStore.subPageData) {
        this.productStore.subPageData = {};
      }

      // 初始化产品特色默认值
      if (!this.productStore.subPageData.productFeatures || this.productStore.subPageData.productFeatures.length === 0) {
        this.productStore.subPageData.productFeatures = [];
      }

      // 初始化具体行程默认值
      if (!this.productStore.subPageData.specificItinerary || this.productStore.subPageData.specificItinerary.length === 0) {
        this.productStore.subPageData.specificItinerary = [];
      }

      // 初始化费用包含默认值
      if (!this.productStore.subPageData.expensesIncluded || this.productStore.subPageData.expensesIncluded.length === 0) {
        this.productStore.subPageData.expensesIncluded = [];
      }

      // 初始化费用不含默认值（改为字符串）
      if (!this.productStore.subPageData.expensesExcluded) {
        this.productStore.subPageData.expensesExcluded = '';
      }

      // 初始化另付费项目默认值（改为字符串）
      if (!this.productStore.subPageData.otherExpenses) {
        this.productStore.subPageData.otherExpenses = '';
      }

      // 初始化自定义提示默认值（改为字符串）
      if (!this.productStore.subPageData.customizationPrompt) {
        this.productStore.subPageData.customizationPrompt = '';
      }

      // 初始化风险提示默认值
      if (!this.productStore.subPageData.riskPrompt || this.productStore.subPageData.riskPrompt.length === 0) {
        this.productStore.subPageData.riskPrompt = [];
      }

      // 初始化自定义预定须知默认值
      if (!this.productStore.subPageData.customNotices || this.productStore.subPageData.customNotices.length === 0) {
        this.productStore.subPageData.customNotices = [];
      }
    },
    isGroupRuleMin(field) {
      return this.groupRulesData[field] <= GROUP_RULES_FIELD_LIMITS[field].min;
    },
    isGroupRuleMax(field) {
      return this.groupRulesData[field] >= GROUP_RULES_FIELD_LIMITS[field].max;
    },
    adjustGroupRulesNumber(field, delta) {
      const limits = GROUP_RULES_FIELD_LIMITS[field];
      if (!limits) {
        return;
      }
      const current = this.normalizeIntegerValue(this.groupRulesData[field]) || 0;
      let next = current + delta;
      if (next < limits.min) next = limits.min;
      if (next > limits.max) next = limits.max;
      this.handleGroupRulesInput(field, { detail: { value: next } });
      this.handleGroupRulesChange(field, { detail: { value: next } });
    },
    syncCategoryTitle() {
      if (this.isCustomCategoryMode) {
        const main = (this.form.customMainCategory || '').trim();
        const sub = (this.form.customSubCategory || '').trim();
        if (main && sub) {
          this.form.title = `${main} - ${sub}`;
        } else if (main) {
          this.form.title = main;
        } else if (sub) {
          this.form.title = sub;
        } else {
          this.form.title = '';
        }
        return;
      }

      if (this.resolvedMainCategory) {
        const mainText = this.resolvedMainCategory.text || '';
        const subText = this.form.currentSubCategoryIndex >= 0
          ? (this.resolvedSubCategories[this.form.currentSubCategoryIndex]?.text || '')
          : '';
        this.form.title = subText ? `${mainText} - ${subText}` : mainText;
      } else {
        this.form.title = '';
      }
    },
    initializeCategorySelectionFromTitle() {
      const title = typeof this.form.title === 'string' ? this.form.title.trim() : '';
      if (!title) {
        this.exitCustomCategoryMode(true);
        return;
      }

      const [main, sub] = title.split(' - ').map(part => part.trim());
      const mainIndex = this.categoryOptions.findIndex(cat => cat.text === main);

      if (mainIndex !== -1) {
        this.exitCustomCategoryMode();
        this.form.currentMainCategoryIndex = mainIndex;
        const subList = this.categoryOptions[mainIndex].child || [];
        if (sub) {
          const subIndex = subList.findIndex(item => item.text === sub);
          this.form.currentSubCategoryIndex = subIndex >= 0 ? subIndex : (subList.length > 0 ? 0 : -1);
        } else {
          this.form.currentSubCategoryIndex = subList.length > 0 ? 0 : -1;
        }
        return;
      }

      this.enterCustomCategoryMode(false);
      this.form.customMainCategory = main || title;
      this.form.customSubCategory = sub || '';
    },
    enterCustomCategoryMode(resetFields = false) {
      this.isCustomCategoryMode = true;
      this.form.currentMainCategoryIndex = -1;
      this.form.currentSubCategoryIndex = -1;
      if (resetFields) {
        this.form.customMainCategory = '';
        this.form.customSubCategory = '';
      }
    },
    exitCustomCategoryMode(resetFields = false) {
      this.isCustomCategoryMode = false;
      if (resetFields) {
        this.form.currentMainCategoryIndex = -1;
        this.form.currentSubCategoryIndex = -1;
      }
      this.form.customMainCategory = '';
      this.form.customSubCategory = '';
    },
  }
};
</script>

<style lang="scss">
.app-container {
  display: flex;
  flex-direction: column;
}

.content {
  padding: 10rpx 0 150rpx 0;
  flex: 1;
  background: #f5f5f5;
}

.card {
  background: #fff;
  border-radius: 16rpx;
  padding: 24rpx;
  margin: 0 20rpx 20rpx 20rpx;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
}

.card-header {
  font-weight: bold;
  font-size: 28rpx;
  margin: 10rpx 0rpx 20rpx 0;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.card-header.expandable {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.arrow {
  font-size: 24rpx;
  color: #888;
}

.card-body {
  font-size: 26rpx;
  // margin-right: 20rpx;
  color: #333;
}

.tips {
  font-size: 24rpx;
  color: #888;
  margin-top: 8rpx;
}

.flex-between {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.image-count {
  font-size: 24rpx;
  color: #666;
  margin-bottom: 0;
  text-align: right;
}

.image-upload {
  display: flex;
  flex-wrap: wrap;
  width: 100%;
}

.image-item {
  position: relative;
  width: 22%;
  padding-bottom: 22%;
  margin-right: 4%;
  margin-bottom: 4%;

  &:nth-child(4n) {
    margin-right: 0;
  }
}

.image-item image {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;

}

/* 图片加载失败占位符样式 */
.image-error-placeholder {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #f5f5f5;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #999;
  font-size: 24rpx;
  border-radius: 8rpx;
  z-index: 1;
}

.delete {
  position: absolute;
  top: 4rpx;
  right: 4rpx;
  background: rgba(0, 0, 0, 0.6);
  color: #fff;
  border-radius: 50%;
  width: 40rpx;
  height: 40rpx;
  text-align: center;
  line-height: 40rpx;
  font-size: 24rpx;
}

.delete.disabled {
  background: rgba(128, 128, 128, 0.6);
  color: #ccc;
  cursor: not-allowed;
}

.image-info {
  position: absolute;
  bottom: 4rpx;
  left: 4rpx;
  right: 4rpx;
  background: rgba(0, 0, 0, 0.7);
  color: #fff;
  padding: 4rpx 8rpx;
  font-size: 20rpx;
  text-align: center;
}

.uploader-text {
  font-size: 20rpx;
}

.upload-btn {
  position: relative;
  width: 22%;
  padding-bottom: 22%;
  // margin-right: 4%;
  margin-bottom: 4%;
  border: 2rpx dashed #ccc;
  border-radius: 8rpx;
  box-sizing: border-box;
}

.upload-btn::before {
  content: '+';
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  font-size: 48rpx;
  color: #999;
}

/* 分类容器样式 - 用于将主标签和子标签放在同一行 */
.category-container {
  display: flex;
  align-items: center;
  gap: 32rpx;
  justify-content: flex-start;
  flex-wrap: wrap;
}

/* 每个分类项的样式 */
.category-item {
  position: relative;
  min-width: 260rpx;
}

.category-picker {
  background: #ffffff;
  border: 1rpx solid #e5e5e5;
  border-radius: 16rpx;
  font-size: 26rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 80rpx;
  color: #333333;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
  gap: 12rpx;
}

.category-picker__text {
  flex: none;
  text-align: center;
  max-width: 320rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: #333333;
}

.category-picker__text.is-placeholder {
  color: #bbbbbb;
}

.category-picker__arrow {
  width: 28rpx;
  height: 28rpx;
}

/* 选中状态：整体颜色改为蓝色 #007aff */
.category-picker--selected {
  border-color: #007aff;
  background-color: #f0f7ff;
}

.category-picker--selected .category-picker__text {
  color: #007aff;
}

.category-picker--selected .category-picker__arrow {
  opacity: 0.8;
}

/* 禁用状态：保持灰色 */
.category-picker--disabled {
  background: #ffffff;
  border-color: #e0e0e0;
  color: #bbbbbb;
}

.category-picker--disabled .category-picker__text {
  color: #bbbbbb;
}

.category-picker:hover {
  border-color: #d0d0d0;
}

.category-picker:active {
  border-color: #c0c0c0;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
}

/* 文字样式规范 - 根据照片设计 */
/* 主标题文字 - 照片中显示的标题样式 */
.content-title {
  font-family: 'Source Han Sans CN', '思源黑体', sans-serif;
  font-size: 32rpx;
  /* 16px */
  font-weight: 400;
  /* 常规字重 */
  color: #303030;
  letter-spacing: 2px;
  /* 照片中显示的字间距 */
  line-height: 1.4;
}

/* 文本域样式 - 自定义字体大小和间距 */
.textarea-input {
	width:100%;
  min-height: 80rpx;
  /* 基本高度 */
  line-height: 1.6;
  /* 增加行高，改善可读性 */
  resize: none;
  /* 禁用手动调整大小 */
  font-family: 'Source Han Sans CN', '思源黑体', sans-serif;
  font-size: 30rpx;
  /* 增大字体大小 */
  color: #303030;
  /* 字体颜色 */
  font-weight: 700;
  /* 常规字重 */
  letter-spacing: 1rpx;
  /* 减少字间距，使文字更紧凑 */
  word-spacing: 2rpx;
  /* 增加单词间距 */
  overflow-y: auto;
  /* 超出时显示滚动条 */
  word-wrap: break-word;
  word-break: break-all;
  border: 1px dashed #3FBDA3;
  /* 可编辑态青色虚线边框 */
  transition: border-color 0.3s ease;
  /* 边框颜色过渡效果 */
  // padding: 16rpx;
  /* 增加内边距，改善视觉效果 */
}

/* 聚焦时加深边框颜色 */
.textarea-input:focus {
  border-color: #2ea88a;
  outline: none;
}

.input-count {
  text-align: right;
  font-size: 24rpx;
  color: #aaa;
  // margin-top: 8rpx;
}

.link {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.link-text {
  color: #007aff;
}

.inner-edit-btn {
  size: mini;
  margin-top: 16rpx;
  background: #007aff;
  color: #fff;
  border-radius: 8rpx;
  padding: 16rpx 24rpx;
}

.section-content {
  padding: 20rpx 0;
  font-size: 28rpx;
  color: #666;
}

.expense-item {
  margin-bottom: 20rpx;
  // padding: 0rpx 16rpx;
  // background-color: #f8f9fa;
  border-radius: 8rpx;
}

.expense-title {
  font-size: 30rpx;
  font-weight: bold;
  color: #333;
  // margin-bottom: 12rpx;
}

.expense-content {
  font-size: 28rpx;
  color: #666;
  line-height: 1.5;
}

.expense-content-text {
  font-size: 28rpx;
  color: #333;
  line-height: 1.6;
  white-space: pre-line;
  word-wrap: break-word;
}


.form-item {
  margin-bottom: 10rpx;
}

.bottom-btns {
  display: flex;
  justify-content: space-around;
  padding: 10rpx;
  background: #fff;
  /* 上边框阴影 */
  box-shadow: 0 -5rpx 16rpx rgba(0, 0, 0, 0.4);
  border-top: 2rpx solid #eee;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 131rpx;
  box-sizing: border-box;
  z-index: 1000;
}

.btn {
  flex: 1;
  margin: 0 10rpx;
  padding: 12rpx 0;
  border-radius: 12rpx;
  font-size: 28rpx;
  height: 85rpx;
  line-height: 60rpx;
}

.preview {
  background: #fff;
  color: #007aff;
  border: 1px solid #007aff;
}

.draft {
  background: #007aff;
  color: #fff;
}

.draft-disabled {
  background: #c9c9c9;
  color: #999;
}

.next {
  background: #c9c9c9;
  color: #333;
}

.next-active {
  background: #007aff;
  color: #fff;
}

.link-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 15rpx 0;
  color: #007aff;
}

.content-preview {
  font-size: 24rpx;
  color: #666;
  flex: 1;
  text-align: right;
  margin-left: 20rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.item-content {
  padding: 8rpx 0;
  font-size: 26rpx;
  color: #333;
  line-height: 1.5;
}

/* 天数卡片样式 - 与itinerary.vue保持一致 */
.day-card {
  background-color: #fff;
  border-radius: 12rpx;
  overflow: hidden;
  box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
  margin-bottom: 20rpx;
}

.day-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: 20rpx 20rpx 20rpx 0;
  border-bottom: 1rpx solid #f0f0f0;
}

.day-tag-container {
  display: flex;
  flex: 1;
  align-items: flex-start;
}

.day-item-title {
  margin-top: 10rpx;
  font-weight: 700;
  font-style: normal;
  font-size: 30rpx;
  color: #303030;
  text-align: left;
}

.day-badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 60rpx;
  height: 60rpx;
  background-color: #3FBDA3;
  color: #fff;
  font-size: 28rpx;
  font-weight: bold;
  height: 64rpx;
  line-height: 64rpx;
  padding: 0rpx 20rpx;
  border-top-right-radius: 5000rpx;
  border-bottom-right-radius: 5000rpx;
  margin-right: 20rpx;
  flex-shrink: 0;
}

.day-details {
  padding: 20rpx;
}

.itinerary-detail-item {
  margin-bottom: 30rpx;
  padding-bottom: 20rpx;
  border-bottom: 1rpx solid #f0f0f0;
}

.itinerary-detail-item:last-child {
  border-bottom: none;
  margin-bottom: 0;
}

.item-time-container {
  display: flex;
  align-items: center;
  // margin-bottom: 16rpx;
  margin-left: 20rpx;
}

.item-time {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  margin-right: 10rpx;
}

.item-type {
  font-size: 28rpx;
  color: #666;
}

.item-separator {
  margin: 0 10rpx;
  color: #999;
}

.item-name {
  font-size: 28rpx;
  color: #666;
  font-weight: 500;
}

/* 地址显示样式 */
.address-display {
  display: flex;
  flex-direction: column;
  gap: 4rpx;
}

.address-name {
  font-size: 26rpx;
  color: #333;
  font-weight: 700;
  line-height: 1.4;
}

.address-location {
  font-size: 24rpx;
  color: #666;
  line-height: 1.4;
  margin-left: 0;
}

.item-details {
  margin-left: 20rpx;
  margin-bottom: 20rpx;
  margin-top: 20rpx;
}

.item-detail-row {
  display: flex;
  // margin-bottom: 10rpx;
  font-size: 24rpx;
  line-height: 1.5;
}

.detail-label {
  color: #999;
  margin-right: 20rpx;
  min-width: 70rpx;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;

  .detail-label-icon {
    width: 28rpx;
    height: 28rpx;
    // margin-top:6rpx;
  }
}

.detail-value {
  color: #666;
  flex: 1;
}

/* ========== 费用包含标题样式 ========== */
.expense-header-content {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}

.expense-title-text {
  color: #F37B1D;
  font-size: 28rpx;
  font-weight: 700;
}

.expense-status-container {
  display: flex;
  align-items: center;
  gap: 8rpx;
}

.expense-status-label {
  background-color: #ffe6d9;
  color: #F37B1D;
  width: 120rpx;
  height: 48rpx;
  text-align: center;
  line-height: 48rpx;
  border-radius: 8rpx;
  font-size: 22rpx;
  font-weight: 500;
}

.expense-status-label-completed {
  background-color: rgba(28, 187, 180, 0.0980392156862745);
  color: #1CBBB4;
  width: 120rpx;
  height: 48rpx;
  text-align: center;
  line-height: 48rpx;
  border-radius: 8rpx;
  font-size: 22rpx;
  font-weight: 500;
}

.expense-arrow {
  color: #999;
  font-size: 24rpx;
  font-weight: bold;
}

.delete-btn {
  margin-left: 20rpx;
  padding: 4rpx 12rpx;
  background-color: #ff4757;
  border-radius: 12rpx;
}

.delete-text {
  color: white;
  font-size: 24rpx;
}

/* ========== 服务详情分割线样式 ========== */
.service-detail-divider {
  display: flex;
  align-items: center;
  margin: 40rpx 100rpx;
  padding: 0;
}

.service-detail-divider::before,
.service-detail-divider::after {
  content: '';
  flex: 1;
  height: 1rpx;
  background-color: #e0e0e0;
}

.service-detail-divider::before {
  margin-right: 40rpx;
}

.service-detail-divider::after {
  margin-left: 40rpx;
}

.service-detail-text {

  font-size: 24rpx;
  color: #a0a0a0;
  white-space: nowrap;
}

/* 行程时间样式 */
.item-time {
  display: block;
  // margin-bottom: 4rpx;
}

/* 行程地点样式 */
.item-address {
  display: block;
}

/* 空行程提示样式 */
.empty-items {
  margin-left: 30rpx;
  color: #999;
  font-size: 24rpx;
  padding: 20rpx 0;
}

.day-items-summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  font-size: 24rpx;
  color: #666;
}

.items-count {
  margin-right: 16rpx;
}

.first-item {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.day-desc {
  font-size: 24rpx;
  color: #666;
  margin-top: 10rpx;
  line-height: 1.5;
}

/* 分类容器样式 - 用于将主标签和子标签放在同一行 */
.category-container {
  display: flex;
  align-items: center;
  gap: 32rpx;
  justify-content: flex-start;
  flex-wrap: wrap;
}

/* 每个分类项的样式 */
.category-item {
  position: relative;
  min-width: 260rpx;
}

.category-picker {
  background: #ffffff;
  border: 1rpx solid #e5e5e5;
  border-radius: 16rpx;
  font-size: 26rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 64rpx;
  color: #333333;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
  gap: 12rpx;
}

.category-picker__text {
  flex: none;
  text-align: center;
  max-width: 320rpx;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: #333333;
}

.category-picker__text.is-placeholder {
  color: #bbbbbb;
}

.category-picker__arrow {
  width: 28rpx;
  height: 28rpx;
}

/* 选中状态：整体颜色改为蓝色 #007aff */
.category-picker--selected {
  border-color: #007aff;
  background-color: #f0f7ff;
}

.category-picker--selected .category-picker__text {
  color: #007aff;
}

.category-picker--selected .category-picker__arrow {
  opacity: 0.8;
}

/* 禁用状态：保持灰色 */
.category-picker--disabled {
  background: #ffffff;
  border-color: #e0e0e0;
  color: #bbbbbb;
}

.category-picker--disabled .category-picker__text {
  color: #bbbbbb;
}

.category-picker:hover {
  border-color: #d0d0d0;
}

.category-picker:active {
  border-color: #c0c0c0;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.06);
}

/* 提示文本样式 */
.tips {
  display: block;
  text-align: center;
  margin-top: 20rpx;
  font-size: 24rpx;
  color: #666;
}

/* 协议链接样式 */
.agreement-links {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  padding: 16rpx 0;
}

.agreement-link {
  color: #007aff;
  font-size: 28rpx;
}

.agreement-text {
  color: #333;
  font-size: 28rpx;
}

.agreement-divider {
  margin: 0 8rpx;
  color: #999;
  font-size: 28rpx;
}

/* 已阅读协议单选框样式 */
.agreement-read {
  margin-top: 20rpx;
}

.radio-group {
  display: flex;
  align-items: center;
}

.radio {
  display: flex;
  align-items: center;
  font-size: 28rpx;
  color: #333;
}

/* 弹窗样式 */
.popup-container {
  background: #fff;
  border-radius: 16rpx 16rpx 0 0;
  overflow: hidden;
}

.popup-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 24rpx;
  border-bottom: 2rpx solid #eee;
}

.popup-title {
  font-size: 32rpx;
  font-weight: bold;
  color: #333;
}

.popup-content {
  height: 500rpx;
  padding: 24rpx;
  box-sizing: border-box;
  overflow-y: auto;
  overflow-x: hidden;
}

.agreement-content {
  font-size: 28rpx;
  line-height: 1.6;
  word-wrap: break-word;
  word-break: break-all;
}

.agreement-title {
  font-size: 32rpx;
  font-weight: bold;
  color: #333;
  display: block;
  margin-bottom: 16rpx;
}

.agreement-date {
  font-size: 24rpx;
  color: #999;
  display: block;
  margin-bottom: 24rpx;
}

.agreement-paragraph {
  display: block;
  margin-bottom: 20rpx;
  color: #333;
  white-space: pre-wrap;
}

.popup-footer {
  padding: 24rpx;
  border-top: 2rpx solid #eee;
}

.popup-btn {
  width: 100%;
  background: #007aff;
  color: #fff;
  border-radius: 8rpx;
  padding: 20rpx 0;
  font-size: 28rpx;
}

/* ========== 产品特色样式 ========== */
/* 可编辑预览区域 - 青色虚线框样式 */
.editable-preview-box {
  width: 100%;
  height: 370rpx;
  /* 固定高度，便于滚动预览 */
  position: relative;
  background: #ffffff;
  border-radius: 12rpx;
  border: 1px dashed #3FBDA3;
  /* 青色虚线边框 */
  display: flex;
  flex-direction: column;
  align-items: stretch;
  justify-content: flex-start;
  cursor: pointer;
  transition: all 0.3s ease;
  box-sizing: border-box;
  overflow: hidden;
}

/* 16:9 固定比例样式 - 用于行程预览区域 */
.editable-preview-box.aspect-ratio-16-9 {
  position: relative;
  width: 100%;
  height: auto;
  padding-bottom: 56.25%;
  /* 16:9 = 9/16 = 56.25% */
  display: block;
  border: 2rpx dashed #3FBDA3;
  border-radius: 12rpx;
  box-sizing: border-box;
  overflow: hidden;
}

/* 成团/退团/违约条款预览样式 */
.editable-preview-box.group-rules-preview {
  position: relative;
  width: 100%;
  height: 400rpx;
  display: block;
  border: 2rpx dashed #3FBDA3;
  border-radius: 12rpx;
  box-sizing: border-box;
  overflow: hidden;
}

/* 有内容时的预览样式 */
.editable-preview-box .preview-content {
  width: 100%;
  height: 100%;
  padding: 20rpx;
  box-sizing: border-box;
}

/* 行程预览区域特定样式 */
.itinerary-day-summary {
  margin-bottom: 20rpx;
}

.day-title {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 10rpx;
}

.day-items-summary {
  padding-left: 20rpx;
}

.items-count {
  font-size: 24rpx;
  color: #666;
  display: block;
  margin-bottom: 5rpx;
}

.first-item {
  font-size: 24rpx;
  color: #999;
}

/* 滚动容器样式 */
.scrollable-content {
  width: 100%;
  height: 100%;
  padding: 10rpx;
  box-sizing: border-box;
  overflow: hidden;
  /* 确保内容不会溢出 */
}

/* 文本内容样式 */
.text-content {
  padding: 10rpx;
  line-height: 1.6;
  white-space: pre-wrap;
}

.guarantee-fixed {
  color: #666666;
  font-family: '思源黑体 Normal', '思源黑体', sans-serif;
  background-color: rgba(242, 242, 242, 0.2);
  padding: 10rpx
}

/* 底部间隔，确保滚动到底部时有足够空间 */
.bottom-spacer {
  height: 30rpx;
}

/* 富文本内容样式 - 确保完整回显 */
.feature-rich-text {
  width: 100%;
  font-size: 26rpx;
  line-height: 1.6;
  color: #333;
  word-break: break-word;
  word-wrap: break-all;
  // padding: 20rpx;
}

/* 有内容时的预览样式 */
.editable-preview-box .preview-content {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 20rpx;
  box-sizing: border-box;
  z-index: 1;
  display: flex;
  flex-direction: column;
}

/* 无内容时的提示样式 */
.editable-preview-box .no-content {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10rpx;
  z-index: 0;
  background: #fcfcfc;
}

/* 预览区域中的文字样式 */
.editable-preview-box .preview-text {
  font-size: 28rpx;
  // color: #3FBDA3;
  color: #999;
  // font-weight: 500;
}

/* 提示文字样式 */
.editable-preview-box .preview-hint {
  font-size: 24rpx;
  color: #999;
}

/* 行程项目详细样式 */
.itinerary-item {
  margin-bottom: 20rpx;
  padding: 15rpx;
  background-color: #ffffff;
  border-left: 4rpx solid #00bcd4;
  border-radius: 8rpx;
}

.item-time {
  font-size: 28rpx;
  // color: #00bcd4;
  // font-weight: bold;
  display: block;
  // margin-bottom: 8rpx;
}

.item-title {
  font-size: 26rpx;
  color: #333;
  font-weight: bold;
  display: block;
  margin-bottom: 8rpx;
}

.item-location {
  font-size: 22rpx;
  color: #666;
  display: block;
  margin-bottom: 8rpx;
}

.item-description {
  font-size: 22rpx;
  color: #999;
  line-height: 1.5;
  display: block;
}

/* 点击时的交互效果 */
.editable-preview-box:active {
  border-color: #2ea88a;
  /* 深青色边框 */
  background: #f0faf7;
}

/* 禁用状态样式 */
.editable-preview-box.disabled {
  opacity: 0.6;
  cursor: not-allowed;
  pointer-events: none;
  border-color: #ccc;
  background: #f5f5f5;
}

/* picker禁用状态样式 */
.picker-value.disabled-picker {
  opacity: 0.6;
  cursor: not-allowed;
  pointer-events: none;
}



.feature-no-content {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
}

.feature-has-content {
  width: 100%;
  min-height: 200rpx;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
}

.feature-rich-text {
  width: 100%;
  font-size: 28rpx;
  line-height: 1.8;
  color: #333333;
}

/* rich-text内部元素样式 */
:deep(.feature-rich-text p) {
  margin-bottom: 20rpx;
  word-break: break-word;
}

:deep(.feature-rich-text img) {
  max-width: 100%;
  height: auto;
  border-radius: 12rpx;
  margin: 10rpx 0;
  display: block;
}

:deep(.feature-rich-text h1) {
  font-size: 36rpx;
  font-weight: bold;
  margin: 20rpx 0;
}

:deep(.feature-rich-text h2) {
  font-size: 32rpx;
  font-weight: bold;
  margin: 18rpx 0;
}

:deep(.feature-rich-text ul),
:deep(.feature-rich-text ol) {
  padding-left: 40rpx;
  margin: 16rpx 0;
}

:deep(.feature-rich-text li) {
  margin-bottom: 10rpx;
}

:deep(.feature-rich-text strong) {
  font-weight: bold;
}

:deep(.feature-rich-text em) {
  font-style: italic;
}

:deep(.feature-rich-text u) {
  text-decoration: underline;
}

.preview-text {
  font-size: 28rpx;
  color: #999999;
  margin-bottom: 8rpx;
}

.preview-hint {
  font-size: 24rpx;
  color: #999999;
}

/* 特色内容样式 */
.feature-item {
  margin-bottom: 20rpx;
}

.feature-text {
  font-size: 28rpx;
  line-height: 1.6;
  color: #333;
  word-break: break-word;
}

.feature-image {
  width: 100%;
  height: auto;
  max-width: 100%;
  border-radius: 12rpx;
  margin: 10rpx 0;
  display: block;
}

/* 违约金表格样式 */
.rule-table {
  width: 100%;
  margin-top: 10rpx;
  border: 1px solid #e0e0e0;
  border-radius: 8rpx;
  overflow: hidden;
}

.rule-table-header {
  display: flex;
  background-color: #f5f5f5;
}

.rule-table-row {
  display: flex;
  border-top: 1px solid #e0e0e0;
}

.rule-table-row:nth-child(even) {
  background-color: #fafafa;
}

.rule-table-cell {
  flex: 1;
  padding: 20rpx 15rpx;
  text-align: center;
  line-height: 36rpx;
  font-size: 28rpx;
}

.rule-table-header .rule-table-cell {
  font-weight: bold;
  color: #333;
}

/* 新增自定义须知按钮样式 */
.add-custom-btn {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  background-color: #fff;
  padding: 15rpx 20rpx;
  border-radius: 8rpx;
  font-size: 28rpx;
  cursor: pointer;
  margin-top: 20rpx;
  transition: all 0.3s ease;
}

.add-custom-btn:active {
  opacity: 0.8;
  transform: scale(0.98);
}

.add-icon-container {
  width: 48rpx;
  height: 48rpx;
  border-radius: 50%;
  background-color: #3FBDA3;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 30rpx;
}

.add-icon {
  font-size: 32rpx;
  font-weight: bold;
  line-height: 1;
  color: #fff;
}

.btn-text {
  color: #07c160;
  font-size: 28rpx;
}

.btn-custom {
  display: flex;
  align-items: center;
  padding: 12rpx 60rpx 12rpx 20rpx;
  background-color: rgba(242, 242, 242, 1);
  border-radius: 8rpx;
}

.btn-text {
  color: #3FBDA3;
  // font-weight: 700;
}


.bottom-text-color {
  color: #666666;
  font-weight: 700;
}

/* 图片裁剪弹窗样式 */
.crop-modal {
  width: 750rpx;
  max-width: 100vw;
  background: #fff;
  border-radius: 16rpx;
  overflow: hidden;
  margin: 0 auto;
  box-sizing: border-box;
}

.crop-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 30rpx 40rpx;
  border-bottom: 1rpx solid #f0f0f0;
}

.crop-title {
  font-size: 32rpx;
  font-weight: 600;
  color: #333;
}

.crop-close {
  font-size: 40rpx;
  color: #999;
  cursor: pointer;
  width: 60rpx;
  height: 60rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.crop-content {
  padding: 40rpx 0;
  text-align: center;
}

.crop-preview-container {
  margin-bottom: 30rpx;
}

.crop-canvas-wrapper {
  position: relative;
  display: block;
  margin: 0 auto 30rpx;
  overflow: hidden;
  border-radius: 8rpx;
  width: 750rpx;
  max-width: 100%;
  box-sizing: border-box;
}

.crop-preview-canvas {
  width: 750rpx;
  height: 750rpx;
  max-width: 100%;
  border: none;
  display: block;
  -webkit-tap-highlight-color: transparent;
  touch-action: none;
  box-sizing: border-box;
}

.crop-frame {
  position: absolute;
  border: 4px solid #007aff;
  cursor: move;
  box-sizing: border-box;
  background-color: rgba(0, 122, 255, 0.05);
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-touch-callout: none;
  min-width: 80px;
  min-height: 80px;
  z-index: 1000;
  pointer-events: none;
  box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.1);
}

.crop-tip-container {
  text-align: center;
  margin-top: 20rpx;
}

.crop-tip {
  font-size: 24rpx;
  color: #666;
  background-color: rgba(0, 0, 0, 0.1);
  padding: 10rpx 20rpx;
  border-radius: 20rpx;
  display: inline-block;
}

.crop-footer {
  display: flex;
  border-top: 1rpx solid #f0f0f0;
}

.crop-btn {
  flex: 1;
  height: 100rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 32rpx;
  cursor: pointer;
  transition: background-color 0.3s ease;
}

.cancel-btn {
  color: #666;
  border-right: 1rpx solid #f0f0f0;
}

.cancel-btn:active {
  background-color: #f5f5f5;
}

.confirm-btn {
  color: #007aff;
  font-weight: 600;
}

.confirm-btn:active {
  background-color: #f0f8ff;
}

/* 移除比例选择器相关样式 */

/* 非阻塞进度条样式 */
.progress-overlay {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 9999;
  pointer-events: none;
  /* 不阻塞用户操作 */
}

.progress-container {
  background: rgba(0, 0, 0, 0.8);
  color: #fff;
  padding: 30rpx 40rpx;
  border-radius: 16rpx;
  min-width: 300rpx;
  text-align: center;
}

.progress-text {
  font-size: 28rpx;
  margin-bottom: 20rpx;
  color: #fff;
}

.progress-bar {
  width: 100%;
  height: 8rpx;
  background-color: rgba(255, 255, 255, 0.3);
  border-radius: 4rpx;
  overflow: hidden;
  margin-bottom: 15rpx;
}

.progress-fill {
  height: 100%;
  background: linear-gradient(90deg, #007aff, #00c389);
  border-radius: 4rpx;
  transition: width 0.3s ease;
}

.progress-percentage {
  font-size: 24rpx;
  color: #fff;
  opacity: 0.8;
}

/* 成团条款弹窗样式 */
.modal-overlay {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 9999;
}

.modal-content {
  background: white;
  border-radius: 20rpx;
  width: 90%;
  max-width: 600rpx;
  max-height: 80vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.modal-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx 30rpx;
  border-bottom: 1rpx solid #f0f0f0;
}

.modal-title {
  font-size: 32rpx;
  font-weight: bold;
  color: #333;
}

.modal-close {
  font-size: 40rpx;
  color: #999;
  cursor: pointer;
}

.modal-body {
  flex: 1;
  padding: 30rpx;
  overflow-y: auto;
}

.form-section {
  margin-bottom: 40rpx;
}

.form-label {
  font-size: 28rpx;
  font-weight: bold;
  color: #333;
  margin-bottom: 10rpx;
}

.mode-options {
  display: flex;
  flex-direction: column;
  gap: 15rpx;
}

.mode-option {
  padding: 10rpx 20rpx;
  border: 2rpx solid #e0e0e0;
  border-radius: 10rpx;
  text-align: center;
  cursor: pointer;
  transition: all 0.3s;
}

.mode-option.active {
  border-color: #3FBDA3;
  background-color: #f0f9f7;
  color: #3FBDA3;
}

.form-row {
  display: flex;
  gap: 20rpx;
  margin-bottom: 10rpx;
}

.form-item {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.form-item-label {
  font-size: 24rpx;
  color: #666;
  margin-bottom: 10rpx;
}

.form-input {
  padding: 15rpx;
  border: 2rpx solid #e0e0e0;
  border-radius: 8rpx;
  font-size: 28rpx;
}

.range-inputs {
  display: flex;
  align-items: center;
  gap: 12rpx;
}

.range-input {
  flex: 1;
}

.range-separator {
  font-size: 28rpx;
  color: #666;
}

.policy-row {
  display: flex;
  gap: 15rpx;
  margin-bottom: 15rpx;
  align-items: end;
}

.policy-item {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.policy-label {
  font-size: 24rpx;
  color: #666;
  margin-bottom: 10rpx;
}

.modal-footer {
  display: flex;
  gap: 20rpx;
  padding: 30rpx;
  border-top: 1rpx solid #f0f0f0;
}

.modal-button {
  flex: 1;
  padding: 20rpx;
  text-align: center;
  border-radius: 10rpx;
  font-size: 28rpx;
  cursor: pointer;
}

.modal-button.cancel {
  background-color: #f5f5f5;
  color: #666;
}

.modal-button.confirm {
  background-color: #3FBDA3;
  color: white;
}

.edit-button {
  padding: 10rpx 20rpx;
  background-color: #3FBDA3;
  color: white;
  border-radius: 8rpx;
  font-size: 24rpx;
  cursor: pointer;
}

.empty-prompt {
  text-align: center;
  color: #999;
  font-size: 28rpx;
  padding: 60rpx 0;
}

/* 退团政策动态操作样式 */
.form-label {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.add-policy-btn {
  padding: 8rpx 16rpx;
  background-color: #3FBDA3;
  color: white;
  border-radius: 6rpx;
  font-size: 24rpx;
  cursor: pointer;
}

.policy-row {
  position: relative;
  padding-right: 80rpx;
}

.delete-policy-btn {
  position: absolute;
  right: 0;
  top: 55%;
  transform: translateY(-50%);
  padding: 8rpx 12rpx;
  background-color: #ff4757;
  color: white;
  border-radius: 6rpx;
  font-size: 22rpx;
  cursor: pointer;
}

.form-row--block {
  flex-direction: column;
  gap: 10rpx;
}

.form-item--full {
  width: 100%;
}

.form-item--inline {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 16rpx;
}

.form-item-label--inline {
  min-width: 160rpx;
  color: #333;
  font-size: 26rpx;
}

.form-input--inline {
  flex: 0 0 55%;
  max-width: 320rpx;
}

.number-input-group {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 20rpx 30rpx 20rpx 0rpx;
  border-top: 1rpx dashed #eee;
  transition: all 0.3s ease;
}

.number-input-group:first-child {
  border-top: none;
}

.number-input-group--confirmation {
  align-items: flex-start;
}

.number-input-group__label {
  font-size: 28rpx;
  color: #333333;
  flex: 1;
  font-weight: 500;
}

.number-input-group__label--multiline {
  display: flex;
  flex-direction: column;
  gap: 8rpx;
}

.number-input-group__label-line {
  font-size: 28rpx;
  color: #333333;
  font-weight: 500;
}

.number-input-group__label-line--sub {
  font-size: 28rpx;
  color: #333;
}

.number-input-group__control {
  display: flex;
  align-items: center;
}

.number-input-group__control--confirmation {
  margin-top: 30rpx;
}

.number-input-group__buttons {
  display: flex;
  align-items: center;
  border-radius: 40rpx;
  padding: 0 10rpx;
  transition: all 0.3s ease;
}

.number-input-decrement,
.number-input-increment {
  width: 40rpx;
  height: 40rpx;
  border-radius: 12rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 28rpx;
  color: #ffffff;
  transition: all 0.3s ease;
  background-color: #FC642F;
}

.number-input-decrement.disabled,
.number-input-increment.disabled {
  background-color: #999999;
}

.number-input-group__input {
  width: 80rpx;
  text-align: center;
  font-size: 28rpx;
  color: #333333;
  border: none;
  background: transparent;
  font-weight: 600;
}

.number-input-group__input::-webkit-outer-spin-button,
.number-input-group__input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.number-input-group__unit {
  font-size: 30rpx;
  color: #333333;
  font-weight: 500;
  margin-left: 16rpx;
}

.number-input-group__range {
  color: #FC642F;
  margin-left: 8rpx;
}

.number-input-group__range-separator {
  margin: 0 8rpx;
}

.number-input-group__range-unit {
  font-size: 24rpx;
  color: #666666;
}

.number-input-group__range-input {
  width: 80rpx;
  text-align: center;
  font-size: 28rpx;
  color: #333333;
  border: none;
  background: transparent;
  font-weight: 600;
}

.number-input-group__range-input::-webkit-outer-spin-button,
.number-input-group__range-input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.custom-category-inputs {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 24rpx;
}

.custom-category-input {
  flex: 1;
  height: 64rpx;
  padding: 0 24rpx;
  border: 1rpx solid #E5E5E5;
  border-radius: 12rpx;
  background: #ffffff;
  font-size: 26rpx;
  color: #1A1A1A;
  box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04);
}

.custom-category-input:disabled {
  background: #f5f5f5;
  color: #b3b3b3;
}
</style>